From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/Frontends/VBoxManage/.scm-settings | 29 + src/VBox/Frontends/VBoxManage/Makefile.kmk | 413 + .../Frontends/VBoxManage/VBoxInternalManage.cpp | 2114 ++++ src/VBox/Frontends/VBoxManage/VBoxManage.cpp | 950 ++ src/VBox/Frontends/VBoxManage/VBoxManage.h | 313 + src/VBox/Frontends/VBoxManage/VBoxManage.rc | 61 + .../Frontends/VBoxManage/VBoxManageAppliance.cpp | 2916 +++++ .../VBoxManage/VBoxManageBandwidthControl.cpp | 384 + src/VBox/Frontends/VBoxManage/VBoxManageCloud.cpp | 2654 ++++ .../VBoxManage/VBoxManageCloudMachine.cpp | 1482 +++ .../Frontends/VBoxManage/VBoxManageControlVM.cpp | 2287 ++++ .../Frontends/VBoxManage/VBoxManageDHCPServer.cpp | 1343 ++ .../Frontends/VBoxManage/VBoxManageDebugVM.cpp | 976 ++ src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp | 2753 +++++ .../Frontends/VBoxManage/VBoxManageGuestCtrl.cpp | 3707 ++++++ .../Frontends/VBoxManage/VBoxManageGuestCtrl.h | 270 + .../VBoxManage/VBoxManageGuestCtrlListener.cpp | 578 + .../Frontends/VBoxManage/VBoxManageGuestProp.cpp | 601 + src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp | 513 + .../Frontends/VBoxManage/VBoxManageHostonly.cpp | 546 + src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp | 3231 +++++ src/VBox/Frontends/VBoxManage/VBoxManageList.cpp | 2434 ++++ .../Frontends/VBoxManage/VBoxManageMetrics.cpp | 671 + src/VBox/Frontends/VBoxManage/VBoxManageMisc.cpp | 2920 +++++ .../Frontends/VBoxManage/VBoxManageModifyNvram.cpp | 582 + .../Frontends/VBoxManage/VBoxManageModifyVM.cpp | 3646 ++++++ .../Frontends/VBoxManage/VBoxManageNATNetwork.cpp | 702 ++ .../Frontends/VBoxManage/VBoxManageSnapshot.cpp | 670 + .../VBoxManage/VBoxManageStorageController.cpp | 1306 ++ src/VBox/Frontends/VBoxManage/VBoxManageUSB.cpp | 581 + .../Frontends/VBoxManage/VBoxManageUpdateCheck.cpp | 392 + src/VBox/Frontends/VBoxManage/VBoxManageUtils.cpp | 131 + src/VBox/Frontends/VBoxManage/VBoxManageUtils.h | 45 + .../Frontends/VBoxManage/nls/ApprovedLanguages.kmk | 40 + .../Frontends/VBoxManage/nls/VBoxManageNls_ru.ts | 12354 +++++++++++++++++++ .../VBoxManage/nls/VBoxManageNls_xx_YY.ts | 10640 ++++++++++++++++ 36 files changed, 65235 insertions(+) create mode 100644 src/VBox/Frontends/VBoxManage/.scm-settings create mode 100644 src/VBox/Frontends/VBoxManage/Makefile.kmk create mode 100644 src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManage.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManage.h create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManage.rc create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageAppliance.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageBandwidthControl.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageCloud.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageCloudMachine.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageControlVM.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageDHCPServer.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageDebugVM.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.h create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrlListener.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageGuestProp.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageHostonly.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageList.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageMetrics.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageMisc.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageModifyNvram.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageNATNetwork.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageSnapshot.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageStorageController.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageUSB.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageUpdateCheck.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageUtils.cpp create mode 100644 src/VBox/Frontends/VBoxManage/VBoxManageUtils.h create mode 100644 src/VBox/Frontends/VBoxManage/nls/ApprovedLanguages.kmk create mode 100644 src/VBox/Frontends/VBoxManage/nls/VBoxManageNls_ru.ts create mode 100644 src/VBox/Frontends/VBoxManage/nls/VBoxManageNls_xx_YY.ts (limited to 'src/VBox/Frontends/VBoxManage') diff --git a/src/VBox/Frontends/VBoxManage/.scm-settings b/src/VBox/Frontends/VBoxManage/.scm-settings new file mode 100644 index 00000000..014df96e --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/.scm-settings @@ -0,0 +1,29 @@ +# $Id: .scm-settings $ +## @file +# Source code massager settings for VBoxManage. +# + +# +# Copyright (C) 2023 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# SPDX-License-Identifier: GPL-3.0-only +# + +/*: --no-rc-use + diff --git a/src/VBox/Frontends/VBoxManage/Makefile.kmk b/src/VBox/Frontends/VBoxManage/Makefile.kmk new file mode 100644 index 00000000..2a63da42 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/Makefile.kmk @@ -0,0 +1,413 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for VBoxManage (the CLI frontend). +# + +# +# Copyright (C) 2006-2023 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# SPDX-License-Identifier: GPL-3.0-only +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + +include $(PATH_ROOT)/doc/manual/Config.kmk + + +## @todo r=andy Sort this stuff alphabetically! +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-expr defined(VBOX_WITH_VMNET) && "$(KBUILD_TARGET)"=="darwin",VBOX_WITH_VMNET,) \ + $(if $(VBOX_WITH_CLOUD_NET), VBOX_WITH_CLOUD_NET) \ + $(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_VIRTIO_NET_1_0),VBOX_WITH_VIRTIO_NET_1_0) \ + $(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_SHARED_CLIPBOARD),VBOX_WITH_SHARED_CLIPBOARD) \ + $(if $(VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS),VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS) \ + $(if $(VBOX_WITH_IOMMU_AMD),VBOX_WITH_IOMMU_AMD) \ + $(if $(VBOX_WITH_IOMMU_INTEL),VBOX_WITH_IOMMU_INTEL) \ + $(if $(VBOX_WITH_UPDATE_AGENT), VBOX_WITH_UPDATE_AGENT) \ + $(if $(VBOX_WITH_VMSVGA),VBOX_WITH_VMSVGA) \ + $(if $(VBOX_WITH_MAIN_NLS),VBOX_WITH_MAIN_NLS) \ + $(if $(VBOX_WITH_TPM),VBOX_WITH_TPM) \ + $(if $(VBOX_WITH_FULL_VM_ENCRYPTION),VBOX_WITH_FULL_VM_ENCRYPTION) + + +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) \ + ../Common + VBoxManage_INTERMEDIATES = \ + $(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp.h + VBoxManage_SOURCES = \ + VBoxManage.cpp \ + VBoxManageUtils.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 \ + $(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp_en_US.cpp \ + VBoxManageHostonly.cpp \ + VBoxManageInfo.cpp \ + VBoxManageList.cpp \ + VBoxManageMetrics.cpp \ + VBoxManageMisc.cpp \ + VBoxManageModifyVM.cpp \ + VBoxManageModifyNvram.cpp \ + VBoxManageSnapshot.cpp \ + VBoxManageStorageController.cpp \ + $(if $(VBOX_WITH_UPDATE_AGENT),VBoxManageUpdateCheck.cpp) \ + VBoxManageUSB.cpp \ + $(if $(VBOX_WITH_NAT_SERVICE),VBoxManageNATNetwork.cpp,) \ + $(if $(VBOX_WITH_NAT_SERVICE),../../NetworkServices/NetLib/VBoxNetPortForwardString.cpp,) \ + VBoxManageCloud.cpp \ + VBoxManageCloudMachine.cpp \ + ../Common/PasswordInput.cpp + VBoxManage_SOURCES.win = \ + VBoxManage.rc + VBoxManage_LIBS += $(LIB_DDU) + + ifdef VBOX_WITH_VBOXMANAGE_NLS + VBOX_PATH_VBOXMANAGE_SRC := $(PATH_SUB_CURRENT) + include $(PATH_SUB_CURRENT)/nls/ApprovedLanguages.kmk + + VBoxManage_DEFS += VBOX_WITH_VBOXMANAGE_NLS + VBoxManage_INCS += \ + $(PATH_ROOT)/src/VBox/Main/include + VBoxManage_SOURCES += \ + $(PATH_ROOT)/src/VBox/Main/src-all/VirtualBoxTranslator.cpp \ + $(PATH_ROOT)/src/VBox/Main/src-all/QMTranslatorImpl.cpp \ + $(PATH_ROOT)/src/VBox/Main/src-all/GlobalStatusConversion.cpp + + # $(addsuffix /VBoxManageBuiltInHelp.cpp,$(addprefix $(VBoxManage_0_OUTDIR)/,$(VBOX_APPROVED_VBOXMANAGE_DOCBOOK_LANGUAGES))) + #VBoxManage_INTERMEDIATES += \ + # $(addsuffix /VBoxManageBuiltInHelp.h,$(addprefix $(VBoxManage_0_OUTDIR)/,$(VBOX_APPROVED_VBOXMANAGE_DOCBOOK_LANGUAGES))) + + # define qt5 tools for translation + USES += qt5 + + PROGRAMS += VBoxManageNls + VBoxManageNls_TEMPLATE = VBoxNLS + VBoxManageNls_QT_TRANSLATIONS = $(addsuffix .ts,$(addprefix $(VBOX_PATH_VBOXMANAGE_SRC)/nls/VBoxManageNls_,$(VBOX_APPROVED_VBOXMANAGE_LANGUAGES))) + VBoxManageNls_VBOX_ALL_NLS_SOURCES = $(wildcard \ + $(VBOX_PATH_VBOXMANAGE_SRC)/*.h \ + $(VBOX_PATH_VBOXMANAGE_SRC)/*.cpp ) + + updatenls:: makeallnls $(VBOX_PATH_VBOXMANAGE_SRC)/nls/VBoxManageNls_en.ts + + makeallnls:: $(VBoxManageNls_VBOX_ALL_NLS_SOURCES) + $(call MSG_L1,lupdate all languages (nls/*.ts)) + $(QUIET)$(TOOL_QT5_LUPDATE) \ + $^ \ + -ts \ + $(filter-out nls/VBoxManageNls_en.ts, $(VBoxManageNls_QT_TRANSLATIONS)) \ + $(VBOX_PATH_VBOXMANAGE_SRC)/nls/VBoxManageNls_xx_YY.ts + + # fake-main-nls: + # $(foreach file, $(VBoxManageNls_QT_TRANSLATIONS) \ + # ,$(NLTAB)$(SED) -i \ + # -e '/.*<\/source>/h' \ + # -e '/.*<\/source>/p' \ + # -e '/<\/translation>/{' \ + # -e 'x' \ + # -e 's/\(.*\)<\/source>/$(notdir $(file)): \1<\/translation>/' \ + # -e '}' \ + # $(file) ) + + + # Create the English translation file. This is something special cause it will + # contain the plural forms only. + $(VBOX_PATH_VBOXMANAGE_SRC)/nls/VBoxManageNls_en.ts: $(VBoxManageNls_VBOX_ALL_NLS_SOURCES) + $(call MSG_L1,lupdate $@) + $(QUIET)$(TOOL_QT5_LUPDATE) \ + $^ \ + -ts \ + "$@" + $(QUIET)$(SED) -n -i \ + -e '//,/<\/context>/!p' \ + -e '//h' \ + -e '//H' \ + -e '//,/<\/message>/H' \ + -e '/<\/context>/{H;x;/ 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 += \ + $(VBOX_XML_CATALOG) \ + $(VBOX_XML_CATALOG_DOCBOOK) \ + $(VBOX_XML_CATALOG_MANUAL) \ + $(VBOX_XML_ENTITIES) \ + $(addprefix $(VBOX_PATH_MANUAL_OUTBASE)/en_US/, $(VBOX_XML_XREF_TO_TEXT) $(VBOX_XML_XREF_TO_TEXT).cat) \ + $(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp_en_US.cpp \ + $(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp_en_US.cpp.ts \ + $(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),replace-xrefs,en_US)) + + +## +# Emits rules for generating cpp files from man pages. +# +# $(evalcall2 def_vbox_man_generate_cpp_help) +# @param 1 Folder with preprocessed man_VBoxManage_* files +# @param 2 language code. +define def_vbox_man_generate_cpp_help + ifneq ($(2),en_US) + VBOX_DOCBOOK_REFENTRY_TO_C_HELP_$(2) = $(VBOX_PATH_MANUAL_SRC)/$(2)/docbook-refentry-to-C-help.xsl + else + VBOX_DOCBOOK_REFENTRY_TO_C_HELP_$(2) = $(VBOX_DOCBOOK_REFENTRY_TO_C_HELP) + endif + $$(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp_$(2).cpp.ts \ + +| $$(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp_$(2).cpp: \ + $$(VBOX_DOCBOOK_REFENTRY_TO_C_HELP_$(2)) \ + $$(VBOX_DOCBOOK_REFENTRY_TO_C_HELP) \ + $$(addprefix $(1)/,$$(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"' \ + '' \ + 'RT_C_DECLS_BEGIN' \ + '/* make next variables visible outside the module */' \ + 'extern PCRTMSGREFENTRY g_apHelpEntries_$(2)[];' \ + 'extern const uint32_t g_cHelpEntries_$(2);' \ + 'RT_C_DECLS_END' \ + '' \ + $$(foreach refentry,$$(filter %.xml,$$^) \ + ,$$(NLTAB)$$(QUIET)$$(call VBOX_XSLTPROC_WITH_CAT, -a+to "$$@") \ + --path '$(VBOX_PATH_MANUAL_SRC)/$(2) $(VBOX_PATH_MANUAL_SRC)/en_US' $$< $$(refentry)) + $$(QUIET)$$(APPEND) -n "$$@" \ + '' \ + 'PCRTMSGREFENTRY g_apHelpEntries_$(2)[] =' \ + '{' + $$(foreach refentry,$$(filter %.xml,$$^) \ + ,$$(NLTAB)$$(QUIET)$$(APPEND) -n "$$@" \ + ' &g_$$(subst -,_,$$(tolower $$(patsubst man_%,%,$$(notdir $$(basename $$(refentry)))))),') + $$(QUIET)$$(APPEND) -n "$$@" \ + '};' \ + '' \ + 'const uint32_t g_cHelpEntries_$(2) = RT_ELEMENTS(g_apHelpEntries_$(2));' \ + '' + $$(QUIET)$$(CP) --changed -- "$$@" "$$(patsubst %.ts,%,$$@)" +endef +# 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. + +# Generate the .cpp file. +$(evalcall2 def_vbox_man_generate_cpp_help, $(VBoxManage_0_OUTDIR),en_US) + + +# Generate built-in help for all languages (English is implicit). +$(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp.cpp.ts \ ++| $(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp.cpp: \ + $(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp_en_US.cpp \ + $(foreach lang,$(VBOX_APPROVED_VBOXMANAGE_DOCBOOK_LANGUAGES), $(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp_$(lang).cpp ) \ + $(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp.h + $(QUIET)$(APPEND) -tn "$@" \ + '/* Autogenerated, do not edit! */' \ + '' \ + '#include "VBoxManageBuiltInHelp.h"' \ + '' \ + 'RT_C_DECLS_BEGIN' \ + '' \ + 'extern PCRTMSGREFENTRY g_apHelpEntries_en_US[];' \ + 'extern const uint32_t g_cHelpEntries_en_US;' +ifdef VBOX_WITH_VBOXMANAGE_NLS + $(foreach lang,$(VBOX_APPROVED_VBOXMANAGE_DOCBOOK_LANGUAGES) \ + ,$(NLTAB)$(QUIET)$(APPEND) -n "$@" \ + '' \ + 'extern PCRTMSGREFENTRY g_apHelpEntries_$(lang)[];' \ + 'extern const uint32_t g_cHelpEntries_$(lang);') +endif + $(QUIET)$(APPEND) -n "$@" \ + '' \ + 'RT_C_DECLS_END' \ + '' \ + 'HELP_LANG_ENTRY_T const g_aHelpLangEntries[] = ' \ + '{' \ + ' { "en_US", 5, &g_apHelpEntries_en_US[0], &g_cHelpEntries_en_US },' +ifdef VBOX_WITH_VBOXMANAGE_NLS + $(foreach lang,$(VBOX_APPROVED_VBOXMANAGE_DOCBOOK_LANGUAGES) \ + ,$(NLTAB)$(QUIET)$(APPEND) "$@" ' { "$(lang)", $(length $(lang)), &g_apHelpEntries_$(lang)[0], &g_cHelpEntries_$(lang) },' ) +endif + $(QUIET)$(APPEND) -n "$@" \ + '};' \ + '' \ + 'uint32_t const g_cHelpLangEntries = RT_ELEMENTS(g_aHelpLangEntries);' \ + '' \ + 'PCHELP_LANG_ENTRY_T volatile g_pHelpLangEntry = &g_aHelpLangEntries[0];'\ + '' + $(QUIET)$(CP) --changed -- "$@" "$(patsubst %.ts,%,$@)" + + +$(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 ' \ + '#include ' \ + '' \ + '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 "$@" \ + '' \ + 'typedef struct HELP_LANG_ENTRY_T' \ + '{' \ + ' const char *pszLang;' \ + ' size_t cchLang;' \ + ' PCRTMSGREFENTRY *papHelpEntries;' \ + ' uint32_t const *pcHelpEntries;' \ + '} HELP_LANG_ENTRY_T;' \ + 'typedef HELP_LANG_ENTRY_T const *PCHELP_LANG_ENTRY_T;' \ + '' \ + 'extern HELP_LANG_ENTRY_T const g_aHelpLangEntries[];' \ + 'extern const uint32_t g_cHelpLangEntries;' \ + '' \ + 'extern PCHELP_LANG_ENTRY_T volatile g_pHelpLangEntry;' \ + '' \ + 'RT_C_DECLS_END' \ + '' \ + '#endif' \ + '' + $(QUIET)$(CP) --changed -- "$@" "$(patsubst %.ts,%,$@)" + +ifdef VBOX_WITH_VBOXMANAGE_NLS + VBoxManage_BLDDIRS += \ + $(addprefix $(VBoxManage_0_OUTDIR)/,$(VBOX_APPROVED_VBOXMANAGE_DOCBOOK_LANGUAGES)) + + VBoxManage_SOURCES += \ + $(foreach lang, $(VBOX_APPROVED_VBOXMANAGE_DOCBOOK_LANGUAGES) \ + , $(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp_$(lang).cpp) + + VBoxManage_CLEAN += \ + $(foreach lang,$(VBOX_APPROVED_VBOXMANAGE_DOCBOOK_LANGUAGES), \ + $(addprefix $(VBOX_PATH_MANUAL_OUTBASE)/$(lang)/, \ + $(VBOX_XML_XREF_TO_TEXT) \ + $(VBOX_XML_XREF_TO_TEXT).cat \ + )) \ + $(addsuffix .cpp,$(addprefix $(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp_,$(VBOX_APPROVED_VBOXMANAGE_DOCBOOK_LANGUAGES))) \ + $(addsuffix .cpp.ts,$(addprefix $(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp_,$(VBOX_APPROVED_VBOXMANAGE_DOCBOOK_LANGUAGES))) \ + $(foreach file, $(filter man_VBoxManage-%,$(VBOX_MANUAL_XML_REFENTRY_FILES)) \ + , $(addsuffix /$(file),$(addprefix $(VBoxManage_0_OUTDIR)/,$(VBOX_APPROVED_VBOXMANAGE_DOCBOOK_LANGUAGES)))) + + # Preprocess the xml files, applying remarks. + $(foreach lang, $(VBOX_APPROVED_VBOXMANAGE_DOCBOOK_LANGUAGES) \ + , $(foreach file,$(filter man_VBoxManage-%,$(VBOX_MANUAL_XML_REFENTRY_FILES)) \ + , $(evalcall2 def_vbox_refentry_preprocess_for_manpage,$(VBoxManage_0_OUTDIR)/$(lang),$(file) \ + ,$(VBOX_PATH_MANUAL_SRC)/$(lang)/$(file),replace-xrefs,$(lang)))) + + # Generate the .cpp file. + $(foreach lang, $(VBOX_APPROVED_VBOXMANAGE_DOCBOOK_LANGUAGES) \ + , $(evalcall2 def_vbox_man_generate_cpp_help,$(VBoxManage_0_OUTDIR)/$(lang),$(lang))) + + # Ensure $(lang) subfolder in the $(VBOX_PATH_MANUAL_OUTBASE) is created (for section names file) + BLDDIRS += $(addprefix $(VBOX_PATH_MANUAL_OUTBASE)/,$(VBOX_APPROVED_VBOXMANAGE_DOCBOOK_LANGUAGES)) + + # Generate sections names file for $(lang) + $(foreach lang, $(VBOX_APPROVED_VBOXMANAGE_DOCBOOK_LANGUAGES) \ + , $(evalcall2 def_vbox_xref_to_text,$(lang))) + +endif + +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..f290fb57 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp @@ -0,0 +1,2114 @@ +/* $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-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "VBoxManage.h" + +/* Includes for the raw disk stuff. */ +#ifdef RT_OS_WINDOWS +# include +# include +#elif defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) \ + || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) +# include +# include +# include +# include +# include +# include +#endif +#ifdef RT_OS_LINUX +# include +# include +# include +# include /* atoi() */ +#endif /* RT_OS_LINUX */ +#ifdef RT_OS_DARWIN +# include +#endif /* RT_OS_DARWIN */ +#ifdef RT_OS_SOLARIS +# include +# include +# include +#endif /* RT_OS_SOLARIS */ +#ifdef RT_OS_FREEBSD +# include +#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 + +DECLARE_TRANSLATION_CONTEXT(Internal); + + +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; + + +/** @name Syntax diagram category, i.e. the command. + * @{ */ +typedef enum +{ + USAGE_INVALID = 0, + USAGE_I_LOADSYMS, + USAGE_I_LOADMAP, + USAGE_I_SETHDUUID, + USAGE_I_LISTPARTITIONS, + USAGE_I_CREATERAWVMDK, + USAGE_I_MODINSTALL, + USAGE_I_MODUNINSTALL, + USAGE_I_RENAMEVMDK, + USAGE_I_CONVERTTORAW, + USAGE_I_CONVERTHD, + USAGE_I_DUMPHDINFO, + USAGE_I_DEBUGLOG, + USAGE_I_SETHDPARENTUUID, + USAGE_I_PASSWORDHASH, + USAGE_I_GUESTSTATS, + USAGE_I_REPAIRHD, + USAGE_I_ALL +} USAGECATEGORY; +/** @} */ + + +/** + * Print the usage info. + */ +static void printUsageInternal(USAGECATEGORY enmCommand, PRTSTREAM pStrm) +{ + Assert(enmCommand != USAGE_INVALID); + RTStrmPrintf(pStrm, + Internal::tr( + "Usage: VBoxManage internalcommands [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 should only be used to analyse\n" + " problems. It is completely unsupported and will change in\n" + " incompatible ways without warning.\n"), + + (enmCommand == USAGE_I_LOADMAP || enmCommand == USAGE_I_ALL) + ? Internal::tr( + " loadmap
[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") + : "", + (enmCommand == USAGE_I_LOADSYMS || enmCommand == USAGE_I_ALL) + ? Internal::tr( + " loadsyms [delta] [module] [module address]\n" + " This will instruct DBGF to load the given symbol file\n" + " during initialization.\n" + "\n") + : "", + (enmCommand == USAGE_I_SETHDUUID || enmCommand == USAGE_I_ALL) + ? Internal::tr( + " sethduuid []\n" + " Assigns a new UUID to the given image file. This way, multiple copies\n" + " of a container can be registered.\n" + "\n") + : "", + (enmCommand == USAGE_I_SETHDPARENTUUID || enmCommand == USAGE_I_ALL) + ? Internal::tr( + " sethdparentuuid \n" + " Assigns a new parent UUID to the given image file.\n" + "\n") + : "", + (enmCommand == USAGE_I_DUMPHDINFO || enmCommand == USAGE_I_ALL) + ? Internal::tr( + " dumphdinfo \n" + " Prints information about the image at the given location.\n" + "\n") + : "", + (enmCommand == USAGE_I_LISTPARTITIONS || enmCommand == USAGE_I_ALL) + ? Internal::tr( + " listpartitions -rawdisk \n" + " Lists all partitions on .\n" + "\n") + : "", + (enmCommand == USAGE_I_CREATERAWVMDK || enmCommand == USAGE_I_ALL) + ? Internal::tr( + " createrawvmdk --filename --rawdisk \n" + " [--partitions [--mbr ] ]\n" + " [--relative]\n" + " Creates a new VMDK image which gives direct access to a physical hard\n" + " disk on the host. The entire disk can be presented to the guest or\n" + " just specific partitions specified using the --partitions parameter.\n" + " If access to individual partitions is granted, then the --mbr parameter\n" + " can be used to specify an alternative Master Boot Record (MBR) (note\n" + " that the partitioning information in the MBR file is ignored). The\n" + " format of the diskname argument for the --rawdisk parameter varies by\n" + " platform but can be determined using the command:\n" + " VBoxManage list hostdrives\n" + " The output lists the available drives and their partitions along with\n" + " their partition types and sizes.\n" + " On Linux, FreeBSD, and Windows hosts the --relative parameter creates a\n" + " VMDK image file which references the specified individual partitions\n" + " directly instead of referencing the partitions by their offset from\n" + " the start of the physical disk.\n" + "\n" + " Nota Bene: The 'createrawvdk' subcommand is deprecated. The equivalent\n" + " functionality is available using the 'VBoxManage createmedium' command\n" + " and should be used instead. See 'VBoxManage help createmedium' for\n" + " details.\n" + "\n") + : "", + (enmCommand == USAGE_I_RENAMEVMDK || enmCommand == USAGE_I_ALL) + ? Internal::tr( + " renamevmdk -from -to \n" + " Renames an existing VMDK image, including the base file and all its extents.\n" + "\n") + : "", + (enmCommand == USAGE_I_CONVERTTORAW || enmCommand == USAGE_I_ALL) +#ifdef ENABLE_CONVERT_RAW_TO_STDOUT + ? Internal::tr( + " converttoraw [-format ] |stdout" + "\n" + " Convert image to raw, writing to file or stdout.\n" + "\n") +#else + ? Internal::tr( + " converttoraw [-format ] " + "\n" + " Convert image to raw, writing to file.\n" + "\n") +#endif + : "", + (enmCommand == USAGE_I_CONVERTHD || enmCommand == USAGE_I_ALL) + ? Internal::tr( + " converthd [-srcformat VDI|VMDK|VHD|RAW]\n" + " [-dstformat VDI|VMDK|VHD|RAW]\n" + " \n" + " converts hard disk images between formats\n" + "\n") + : "", + (enmCommand == USAGE_I_REPAIRHD || enmCommand == USAGE_I_ALL) + ? Internal::tr( + " repairhd [-dry-run]\n" + " [-format VDI|VMDK|VHD|...]\n" + " \n" + " Tries to repair corrupted disk images\n" + "\n") + : "", +#ifdef RT_OS_WINDOWS + (enmCommand == USAGE_I_MODINSTALL || enmCommand == USAGE_I_ALL) + ? Internal::tr( + " modinstall\n" + " Installs the necessary driver for the host OS\n" + "\n") + : "", + (enmCommand == USAGE_I_MODUNINSTALL || enmCommand == USAGE_I_ALL) + ? Internal::tr( + " moduninstall\n" + " Deinstalls the driver\n" + "\n") + : "", +#else + "", + "", +#endif + (enmCommand == USAGE_I_DEBUGLOG || enmCommand == USAGE_I_ALL) + ? Internal::tr( + " debuglog [--enable|--disable] [--flags todo]\n" + " [--groups todo] [--destinations todo]\n" + " Controls debug logging.\n" + "\n") + : "", + (enmCommand == USAGE_I_PASSWORDHASH || enmCommand == USAGE_I_ALL) + ? Internal::tr( + " passwordhash \n" + " Generates a password hash.\n" + "\n") + : "", + (enmCommand == USAGE_I_GUESTSTATS || enmCommand == USAGE_I_ALL) + ? Internal::tr( + " gueststats [--interval ]\n" + " Obtains and prints internal guest statistics.\n" + " Sets the update interval if specified.\n" + "\n") + : "" + ); +} + + +/** + * Print a usage synopsis and the syntax error message. + * @returns RTEXITCODE_SYNTAX. + */ +static RTEXITCODE errorSyntaxInternal(USAGECATEGORY enmCommand, const char *pszFormat, ...) +{ + va_list args; + showLogo(g_pStdErr); // show logo even if suppressed + + printUsageInternal(enmCommand, g_pStdErr); + + va_start(args, pszFormat); + RTStrmPrintf(g_pStdErr, Internal::tr("\nSyntax error: %N\n"), pszFormat, &args); + va_end(args); + return RTEXITCODE_SYNTAX; +} + + +/** + * errorSyntaxInternal for RTGetOpt users. + * + * @returns RTEXITCODE_SYNTAX. + * + * @param enmCommand The command. + * @param vrc The RTGetOpt return code. + * @param pValueUnion The value union. + */ +static RTEXITCODE errorGetOptInternal(USAGECATEGORY enmCommand, int vrc, union RTGETOPTUNION const *pValueUnion) +{ + /* + * Check if it is an unhandled standard option. + */ + if (vrc == 'V') + { + RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision()); + return RTEXITCODE_SUCCESS; + } + + if (vrc == 'h') + { + showLogo(g_pStdErr); + printUsageInternal(enmCommand, g_pStdOut); + return RTEXITCODE_SUCCESS; + } + + /* + * General failure. + */ + showLogo(g_pStdErr); // show logo even if suppressed + + printUsageInternal(enmCommand, g_pStdErr); + + if (vrc == VINF_GETOPT_NOT_OPTION) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, Internal::tr("Invalid parameter '%s'"), pValueUnion->psz); + if (vrc > 0) + { + if (RT_C_IS_PRINT(vrc)) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, Internal::tr("Invalid option -%c"), vrc); + return RTMsgErrorExit(RTEXITCODE_SYNTAX, Internal::tr("Invalid option case %i"), vrc); + } + if (vrc == VERR_GETOPT_UNKNOWN_OPTION) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, Internal::tr("Unknown option: %s"), pValueUnion->psz); + if (vrc == VERR_GETOPT_INVALID_ARGUMENT_FORMAT) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, Internal::tr("Invalid argument format: %s"), pValueUnion->psz); + if (pValueUnion->pDef) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, "%s: %Rrs", pValueUnion->pDef->pszLong, vrc); + return RTMsgErrorExit(RTEXITCODE_SYNTAX, "%Rrs", vrc); +} + + +/** + * Externally visible wrapper around printUsageInternal() to dump the + * complete usage text. + * + * @param pStrm The stream to dump the usage text to. + */ +DECLHIDDEN(void) printUsageInternalCmds(PRTSTREAM pStrm) +{ + printUsageInternal(USAGE_I_ALL, pStrm); +} + + +/** @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 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(Internal::tr("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 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 vrc = RTUtf16ToUtf8(Keys.raw(), &pszKeys); + if (RT_SUCCESS(vrc)) + { + /* 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(Internal::tr("Failed to delete key '%s' from '%s', string conversion error %Rrc!"), + pszKey, pszKeyBase, vrc); + + 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 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(Internal::tr("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 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 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 aVirtualBox, ComPtr aSession) +{ + RT_NOREF(aSession); + HRESULT hrc; + + /* + * Get the VM + */ + ComPtr 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(Internal::tr("Missing the filename argument!\n")); + pszFilename = argv[1]; + + /* offDelta */ + if (argc >= 3) + { + int vrc = RTStrToInt64Ex(argv[2], NULL, 0, &offDelta); + if (RT_FAILURE(vrc)) + return errorArgument(argv[0], Internal::tr("Failed to read delta '%s', vrc=%Rrc\n"), argv[2], vrc); + } + + /* pszModule */ + if (argc >= 4) + pszModule = argv[3]; + + /* ModuleAddress */ + if (argc >= 5) + { + int vrc = RTStrToUInt64Ex(argv[4], NULL, 0, &ModuleAddress); + if (RT_FAILURE(vrc)) + return errorArgument(argv[0], Internal::tr("Failed to read module address '%s', vrc=%Rrc\n"), argv[4], vrc); + } + + /* ModuleSize */ + if (argc >= 6) + { + int vrc = RTStrToUInt64Ex(argv[5], NULL, 0, &ModuleSize); + if (RT_FAILURE(vrc)) + return errorArgument(argv[0], Internal::tr("Failed to read module size '%s', vrc=%Rrc\n"), argv[5], vrc); + } + + /* + * Add extra data. + */ + Utf8Str KeyStr; + 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 aVirtualBox, ComPtr aSession) +{ + RT_NOREF(aSession); + HRESULT hrc; + + /* + * Get the VM + */ + ComPtr 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(Internal::tr("Missing the filename argument!\n")); + pszFilename = argv[1]; + + /* address */ + if (argc < 3) + return errorArgument(Internal::tr("Missing the module address argument!\n")); + int vrc = RTStrToUInt64Ex(argv[2], NULL, 0, &ModuleAddress); + if (RT_FAILURE(vrc)) + return errorArgument(argv[0], Internal::tr("Failed to read module address '%s', vrc=%Rrc\n"), argv[2], vrc); + + /* name (optional) */ + if (argc > 3) + pszModule = argv[3]; + + /* subtrahend (optional) */ + if (argc > 4) + { + vrc = RTStrToUInt64Ex(argv[4], NULL, 0, &offSubtrahend); + if (RT_FAILURE(vrc)) + return errorArgument(argv[0], Internal::tr("Failed to read subtrahend '%s', vrc=%Rrc\n"), argv[4], vrc); + } + + /* segment (optional) */ + if (argc > 5) + { + vrc = RTStrToUInt32Ex(argv[5], NULL, 0, &iSeg); + if (RT_FAILURE(vrc)) + return errorArgument(argv[0], Internal::tr("Failed to read segment number '%s', vrc=%Rrc\n"), argv[5], vrc); + } + + /* + * Add extra data. + */ + Utf8Str KeyStr; + 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 vrc, RT_SRC_POS_DECL, const char *pszFormat, va_list va) +{ + RT_NOREF(pvUser); + RTMsgErrorV(pszFormat, va); + RTMsgError(Internal::tr("Error code %Rrc at %s(%u) in function %s"), vrc, 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 aVirtualBox, ComPtr 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 errorSyntaxInternal(USAGE_I_SETHDUUID, Internal::tr("Not enough parameters")); + /* if specified, take UUID, otherwise generate a new one */ + if (argc == 3) + { + if (RT_FAILURE(RTUuidFromStr(&rtuuid, argv[2]))) + return errorSyntaxInternal(USAGE_I_SETHDUUID, Internal::tr("Invalid UUID parameter")); + uuid = argv[2]; + } else + uuid.create(); + } + else if (!strcmp(argv[0], "sethdparentuuid")) + { + uuidType = HDPARENTUUID; + if (argc != 3) + return errorSyntaxInternal(USAGE_I_SETHDPARENTUUID, Internal::tr("Not enough parameters")); + if (RT_FAILURE(RTUuidFromStr(&rtuuid, argv[2]))) + return errorSyntaxInternal(USAGE_I_SETHDPARENTUUID, Internal::tr("Invalid UUID parameter")); + uuid = argv[2]; + } + else + return errorSyntaxInternal(USAGE_I_SETHDUUID, Internal::tr("Invalid invocation")); + + /* just try it */ + char *pszFormat = NULL; + VDTYPE enmType = VDTYPE_INVALID; + int vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */, argv[1], VDTYPE_INVALID, &pszFormat, &enmType); + if (RT_FAILURE(vrc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Format autodetect failed: %Rrc"), vrc); + + PVDISK pDisk = NULL; + + 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); + + vrc = VDCreate(pVDIfs, enmType, &pDisk); + if (RT_FAILURE(vrc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot create the virtual disk container: %Rrc"), vrc); + + /* Open the image */ + vrc = VDOpen(pDisk, pszFormat, argv[1], VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_INFO, NULL); + if (RT_FAILURE(vrc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot open the image: %Rrc"), vrc); + + if (uuidType == HDUUID) + vrc = VDSetUuid(pDisk, VD_LAST_IMAGE, uuid.raw()); + else + vrc = VDSetParentUuid(pDisk, VD_LAST_IMAGE, uuid.raw()); + if (RT_FAILURE(vrc)) + RTMsgError(Internal::tr("Cannot set a new UUID: %Rrc"), vrc); + else + RTPrintf(Internal::tr("UUID changed to: %s\n"), uuid.toString().c_str()); + + VDCloseAll(pDisk); + + return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + + +static RTEXITCODE CmdDumpHDInfo(int argc, char **argv, ComPtr aVirtualBox, ComPtr aSession) +{ + RT_NOREF(aVirtualBox, aSession); + + /* we need exactly one parameter: the image file */ + if (argc != 1) + { + return errorSyntaxInternal(USAGE_I_DUMPHDINFO, Internal::tr("Not enough parameters")); + } + + /* just try it */ + char *pszFormat = NULL; + VDTYPE enmType = VDTYPE_INVALID; + int vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */, argv[0], VDTYPE_INVALID, &pszFormat, &enmType); + if (RT_FAILURE(vrc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Format autodetect failed: %Rrc"), vrc); + + PVDISK pDisk = NULL; + + 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); + + vrc = VDCreate(pVDIfs, enmType, &pDisk); + if (RT_FAILURE(vrc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot create the virtual disk container: %Rrc"), vrc); + + /* Open the image */ + vrc = VDOpen(pDisk, pszFormat, argv[0], VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO, NULL); + if (RT_FAILURE(vrc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot open the image: %Rrc"), vrc); + + VDDumpImages(pDisk); + + VDCloseAll(pDisk); + + return RT_SUCCESS(vrc) ? 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; + + VDISKPARTTYPE partitioningType; + + pPart->cPartitions = 0; + memset(pPart->aPartitions, '\0', sizeof(pPart->aPartitions)); + + int vrc = RTFileReadAt(File, 0, &aBuffer, sizeof(aBuffer), NULL); + if (RT_FAILURE(vrc)) + return vrc; + + if (aBuffer[450] == 0xEE)/* check the sign of the GPT disk*/ + { + partitioningType = VDISKPARTTYPE_GPT; + pPart->uPartitioningType = VDISKPARTTYPE_GPT;//partitioningType; + + if (aBuffer[510] != 0x55 || aBuffer[511] != 0xaa) + return VERR_INVALID_PARAMETER; + + vrc = RTFileReadAt(File, sector_size, &partitionTableHeader, sector_size, NULL); + if (RT_SUCCESS(vrc)) + { + /** @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(Internal::tr("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(Internal::tr("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. */ + vrc = RTFileReadAt(File, 1024, pbPartTable, RT_ALIGN_Z(partitionEntrySize * partitionsNumber, 512), NULL); + if (RT_FAILURE(vrc)) + { + RTMsgError(Internal::tr("Reading the partition table failed")); + RTMemFree(pbPartTable); + return vrc; + } + + 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 = VDISKPARTTYPE_MBR; + pPart->uPartitioningType = VDISKPARTTYPE_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(Internal::tr("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(Internal::tr("Inconsistency for logical partition start")); + return VERR_INVALID_PARAMETER; + } + + do + { + vrc = RTFileReadAt(File, (uStart + uOffset) * 512, &aBuffer, sizeof(aBuffer), NULL); + if (RT_FAILURE(vrc)) + return vrc; + + if (aBuffer[510] != 0x55 || aBuffer[511] != 0xaa) + { + RTMsgError(Internal::tr("Logical partition without magic")); + return VERR_INVALID_PARAMETER; + } + uint8_t *p = &aBuffer[0x1be]; + + if (p[4] == 0) + { + RTMsgError(Internal::tr("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(Internal::tr("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(Internal::tr("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(Internal::tr("Two partitions start at the same place")); + return VERR_INVALID_PARAMETER; + } + else if (pPart->aPartitions[j].uStart == 0) + { + RTMsgError(Internal::tr("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 == VDISKPARTTYPE_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(Internal::tr("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(Internal::tr("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 aVirtualBox, ComPtr 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(Internal::tr("Missing argument to '%s'"), argv[i]); + } + i++; + rawdisk = argv[i]; + } + else + { + return errorSyntaxInternal(USAGE_I_LISTPARTITIONS, Internal::tr("Invalid parameter '%s'"), argv[i]); + } + } + + if (rawdisk.isEmpty()) + return errorSyntaxInternal(USAGE_I_LISTPARTITIONS, Internal::tr("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, Internal::tr("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(Internal::tr("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 const RTGETOPTDEF g_aCreateRawVMDKOptions[] = +{ + { "--filename", 'f', RTGETOPT_REQ_STRING }, + { "-filename", 'f', RTGETOPT_REQ_STRING }, + { "--rawdisk", 'd', RTGETOPT_REQ_STRING }, + { "-rawdisk", 'd', RTGETOPT_REQ_STRING }, + { "--partitions", 'p', RTGETOPT_REQ_STRING }, + { "-partitions", 'p', RTGETOPT_REQ_STRING }, + { "--mbr", 'm', RTGETOPT_REQ_STRING }, + { "-mbr", 'm', RTGETOPT_REQ_STRING }, +#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_WINDOWS) + { "--relative", 'r', RTGETOPT_REQ_NOTHING }, + { "-relative", 'r', RTGETOPT_REQ_NOTHING }, +#endif /* RT_OS_LINUX || RT_OS_FREEBSD || RT_OS_WINDOWS */ +}; + +static RTEXITCODE CmdCreateRawVMDK(int argc, char **argv, HandlerArg *a) +{ + const char *pszFilename = NULL; + const char *pszRawdisk = NULL; + const char *pszPartitions = NULL; + const char *pszMbr = NULL; + bool fRelative = false; + int c; + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, argc, argv, g_aCreateRawVMDKOptions, RT_ELEMENTS(g_aCreateRawVMDKOptions), 0, 0); + while ((c = RTGetOpt(&GetState, &ValueUnion))) + { + switch (c) + { + case 'f': // --filename + pszFilename = ValueUnion.psz; + break; + + case 'd': // --rawdisk + pszRawdisk = ValueUnion.psz; + break; + + case 'p': // --partitions + pszPartitions = ValueUnion.psz; + break; + + case 'm': // --mbr + pszMbr = ValueUnion.psz; + break; +#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_WINDOWS) + case 'r': // --relative + fRelative = true; + break; +#endif /* RT_OS_LINUX || RT_OS_FREEBSD || RT_OS_WINDOWS */ + + default: + return errorGetOptInternal(USAGE_I_CREATERAWVMDK, c, &ValueUnion); + } + } + + if (!pszFilename || !*pszFilename) + return errorSyntaxInternal(USAGE_I_CREATERAWVMDK, Internal::tr("Mandatory parameter --filename missing")); + if (!pszRawdisk || !*pszRawdisk) + return errorSyntaxInternal(USAGE_I_CREATERAWVMDK, Internal::tr("Mandatory parameter --rawdisk missing")); + if (!pszPartitions && pszMbr) + return errorSyntaxInternal(USAGE_I_CREATERAWVMDK, + Internal::tr("The parameter --mbr is only valid when the parameter -partitions is also present")); + + /* Construct the equivalent 'VBoxManage createmedium disk --variant RawDisk ...' command line. */ + size_t cMaxArgs = 9; /* all possible 'createmedium' args based on the 'createrawvmdk' options + 1 for NULL */ + char **papszNewArgv = (char **)RTMemAllocZ(sizeof(papszNewArgv[0]) * cMaxArgs); + if (!papszNewArgv) + return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Failed to allocate memory for argument array")); + int cArgs = 0; + + papszNewArgv[cArgs++] = RTStrDup("disk"); + papszNewArgv[cArgs++] = RTStrDup("--variant=RawDisk"); + papszNewArgv[cArgs++] = RTStrDup("--format=VMDK"); + + for (int i = 0; i < cArgs; i++) + if (!papszNewArgv[i]) + return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Failed to allocate memory for argument array")); + + if ( RTStrAPrintf(&papszNewArgv[cArgs++], "--filename=%s", pszFilename) == -1 + || RTStrAPrintf(&papszNewArgv[cArgs++], "--property=RawDrive=%s", pszRawdisk) == -1 + || (pszPartitions && (RTStrAPrintf(&papszNewArgv[cArgs++], "--property=Partitions=%s", pszPartitions) == -1)) + || (pszMbr && (RTStrAPrintf(&papszNewArgv[cArgs++], "--property-filename=%s", pszMbr) == -1)) + || (fRelative && (RTStrAPrintf(&papszNewArgv[cArgs++], "--property=Relative=%d", fRelative) == -1))) + return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Failed to allocate memory for argument array")); + + papszNewArgv[cArgs] = NULL; + + RTStrmPrintf(g_pStdErr, + Internal::tr("\nThe 'createrawvdk' subcommand is deprecated. The equivalent functionality is\n" + "available using the 'VBoxManage createmedium' command and should be used\n" + "instead. See 'VBoxManage help createmedium' for details.\n\n")); + + a->argc = cArgs; + a->argv = papszNewArgv; + RTEXITCODE rcExit = handleCreateMedium(a); + + for (int i = 0; i < cArgs; i++) + RTStrFree(papszNewArgv[i]); + RTMemFree(papszNewArgv); + + return rcExit; +} + +static RTEXITCODE CmdRenameVMDK(int argc, char **argv, ComPtr aVirtualBox, ComPtr 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(Internal::tr("Missing argument to '%s'"), argv[i]); + } + i++; + src = argv[i]; + } + else if (strcmp(argv[i], "-to") == 0) + { + if (argc <= i + 1) + { + return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]); + } + i++; + dst = argv[i]; + } + else + { + return errorSyntaxInternal(USAGE_I_RENAMEVMDK, Internal::tr("Invalid parameter '%s'"), argv[i]); + } + } + + if (src.isEmpty()) + return errorSyntaxInternal(USAGE_I_RENAMEVMDK, Internal::tr("Mandatory parameter -from missing")); + if (dst.isEmpty()) + return errorSyntaxInternal(USAGE_I_RENAMEVMDK, Internal::tr("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, Internal::tr("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(Internal::tr("Cannot rename the image: %Rrc"), vrc); + } + else + RTMsgError(Internal::tr("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 aVirtualBox, ComPtr 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(Internal::tr("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 errorSyntaxInternal(USAGE_I_CONVERTTORAW, Internal::tr("Invalid parameter '%s'"), argv[i]); + } + } + + if (src.isEmpty()) + return errorSyntaxInternal(USAGE_I_CONVERTTORAW, Internal::tr("Mandatory filename parameter missing")); + if (dst.isEmpty()) + return errorSyntaxInternal(USAGE_I_CONVERTTORAW, Internal::tr("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, Internal::tr("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, Internal::tr("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(), VDTYPE_INVALID, &pszFormat, &enmType); + if (RT_FAILURE(vrc) || enmType != VDTYPE_HDD) + { + VDCloseAll(pDisk); + if (!fWriteToStdOut) + { + RTFileClose(outFile); + RTFileDelete(dst.c_str()); + } + if (RT_FAILURE(vrc)) + RTMsgError(Internal::tr("No file format specified and autodetect failed - please specify format: %Rrc"), + vrc); + else + RTMsgError(Internal::tr("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, Internal::tr("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, Internal::tr("Converting image \"%s\" with size %RU64 bytes (%RU64MB) to raw...\n", "", cbSize), + 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, Internal::tr("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, Internal::tr("Out of memory allocating read buffer")); + } + + if (!fWriteToStdOut) + RTFileClose(outFile); + VDCloseAll(pDisk); + return RTEXITCODE_SUCCESS; +} + +static RTEXITCODE CmdConvertHardDisk(int argc, char **argv, ComPtr aVirtualBox, ComPtr 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(Internal::tr("Missing argument to '%s'"), argv[i]); + } + i++; + srcformat = argv[i]; + } + else if (strcmp(argv[i], "-dstformat") == 0) + { + if (argc <= i + 1) + { + return errorArgument(Internal::tr("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 errorSyntaxInternal(USAGE_I_CONVERTHD, Internal::tr("Invalid parameter '%s'"), argv[i]); + } + } + + if (src.isEmpty()) + return errorSyntaxInternal(USAGE_I_CONVERTHD, Internal::tr("Mandatory input image parameter missing")); + if (dst.isEmpty()) + return errorSyntaxInternal(USAGE_I_CONVERTHD, Internal::tr("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(), VDTYPE_HDD, &pszFormat, &enmSrcType); + if (RT_FAILURE(vrc)) + { + RTMsgError(Internal::tr("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(Internal::tr("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(Internal::tr("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(Internal::tr("Cannot create the destination virtual disk container: %Rrc"), vrc); + break; + } + + uint64_t cbSize = VDGetSize(pSrcDisk, VD_LAST_IMAGE); + RTStrmPrintf(g_pStdErr, Internal::tr("Converting image \"%s\" with size %RU64 bytes (%RU64MB)...\n", "", cbSize), + 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(Internal::tr("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 aVirtualBox, ComPtr 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(Internal::tr("Missing argument to '%s'"), argv[i]); + } + i++; + format = argv[i]; + } + else if (image.isEmpty()) + { + image = argv[i]; + } + else + { + return errorSyntaxInternal(USAGE_I_REPAIRHD, Internal::tr("Invalid parameter '%s'"), argv[i]); + } + } + + if (image.isEmpty()) + return errorSyntaxInternal(USAGE_I_REPAIRHD, Internal::tr("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(), VDTYPE_HDD, &pszFormat, &enmSrcType); + if (RT_FAILURE(vrc) && (vrc != VERR_VD_IMAGE_CORRUPTED)) + { + RTMsgError(Internal::tr("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 vrc = SUPR3Uninstall(); + if (RT_SUCCESS(vrc) || vrc == VERR_NOT_IMPLEMENTED) + return RTEXITCODE_SUCCESS; + return RTEXITCODE_FAILURE; +} + +/** + * Loads the necessary driver. + * + * @returns VBox status code + */ +static RTEXITCODE CmdModInstall(void) +{ + int vrc = SUPR3Install(); + if (RT_SUCCESS(vrc) || vrc == VERR_NOT_IMPLEMENTED) + return RTEXITCODE_SUCCESS; + return RTEXITCODE_FAILURE; +} + +static RTEXITCODE CmdDebugLog(int argc, char **argv, ComPtr aVirtualBox, ComPtr 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 errorSyntaxInternal(USAGE_I_DEBUGLOG, Internal::tr("Missing VM name/UUID")); + + ComPtr ptrMachine; + HRESULT hrc; + 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 ptrConsole; + CHECK_ERROR_RET(aSession, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE); + + ComPtr 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 errorGetOptInternal(USAGE_I_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(Internal::tr("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 aVirtualBox, ComPtr aSession) +{ + RT_NOREF(aVirtualBox, aSession); + + /* one parameter, the password to hash */ + if (argc != 1) + return errorSyntaxInternal(USAGE_I_PASSWORDHASH, Internal::tr("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(Internal::tr("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 aVirtualBox, ComPtr aSession) +{ + /* one parameter, guest name */ + if (argc < 1) + return errorSyntaxInternal(USAGE_I_GUESTSTATS, Internal::tr("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 errorGetOptInternal(USAGE_I_GUESTSTATS, ch, &ValueUnion); + } + } + + if (argc > 1 && aUpdateInterval == 0) + return errorSyntaxInternal(USAGE_I_GUESTSTATS, Internal::tr("Invalid update interval specified")); + + RTPrintf(Internal::tr("argc=%d interval=%u\n"), argc, aUpdateInterval); + + ComPtr ptrMachine; + HRESULT hrc; + 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 ptrConsole; + CHECK_ERROR_RET(aSession, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE); + + ComPtr 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) +{ + /* at least a command is required */ + if (a->argc < 1) + return errorSyntaxInternal(USAGE_I_ALL, Internal::tr("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); + 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 errorSyntaxInternal(USAGE_I_ALL, Internal::tr("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..1a59c868 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManage.cpp @@ -0,0 +1,950 @@ +/* $Id: VBoxManage.cpp $ */ +/** @file + * VBoxManage - VirtualBox's command-line interface. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef VBOX_WITH_VBOXMANAGE_NLS +# include +# include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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_INTERNAL HELP_CMD_VBOXMANAGE_INVALID + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ + +/** + * VBoxManage command descriptor. + */ +typedef struct VBMGCMD +{ + /** The command. */ + const char *pszCommand; + /** 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; + + +DECLARE_TRANSLATION_CONTEXT(VBoxManage); + +void setBuiltInHelpLanguage(const char *pszLang); + +#ifdef VBOX_WITH_VBOXMANAGE_NLS +/* listener class for language updates */ +class VBoxEventListener +{ +public: + VBoxEventListener() + {} + + + HRESULT init(void *) + { + return S_OK; + } + + HRESULT init() + { + return S_OK; + } + + void uninit() + { + } + + virtual ~VBoxEventListener() + { + } + + STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent) + { + switch(aType) + { + case VBoxEventType_OnLanguageChanged: + { + /* + * Proceed with uttmost care as we might be racing com::Shutdown() + * and have the ground open up beneath us. + */ + LogFunc(("VBoxEventType_OnLanguageChanged\n")); + VirtualBoxTranslator *pTranslator = VirtualBoxTranslator::tryInstance(); + if (pTranslator) + { + ComPtr pEvent = aEvent; + Assert(pEvent); + + /* This call may fail if we're racing COM shutdown. */ + com::Bstr bstrLanguageId; + HRESULT hrc = pEvent->COMGETTER(LanguageId)(bstrLanguageId.asOutParam()); + if (SUCCEEDED(hrc)) + { + try + { + com::Utf8Str strLanguageId(bstrLanguageId); + LogFunc(("New language ID: %s\n", strLanguageId.c_str())); + pTranslator->i_loadLanguage(strLanguageId.c_str()); + setBuiltInHelpLanguage(strLanguageId.c_str()); + } + catch (std::bad_alloc &) + { + LogFunc(("Caught bad_alloc")); + } + } + else + LogFunc(("Failed to get new language ID: %Rhrc\n", hrc)); + + pTranslator->release(); + } + break; + } + + default: + AssertFailed(); + } + + return S_OK; + } +}; + +typedef ListenerImpl VBoxEventListenerImpl; + +VBOX_LISTENER_DECLARE(VBoxEventListenerImpl) +#endif /* !VBOX_WITH_VBOXMANAGE_NLS */ + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/*extern*/ bool g_fDetailedProgress = false; +/** Set by the signal handler. */ +static volatile bool g_fCanceled = false; + + +/** + * All registered command handlers + */ +static const VBMGCMD g_aCommands[] = +{ + { "internalcommands", VBMG_CMD_INTERNAL, handleInternalCommands, 0 }, + { "list", HELP_CMD_LIST, handleList, 0 }, + { "showvminfo", HELP_CMD_SHOWVMINFO, handleShowVMInfo, 0 }, + { "registervm", HELP_CMD_REGISTERVM, handleRegisterVM, 0 }, + { "unregistervm", HELP_CMD_UNREGISTERVM, handleUnregisterVM, 0 }, + { "clonevm", HELP_CMD_CLONEVM, handleCloneVM, 0 }, + { "movevm", HELP_CMD_MOVEVM, handleMoveVM, 0 }, +#ifdef VBOX_WITH_FULL_VM_ENCRYPTION + { "encryptvm", HELP_CMD_ENCRYPTVM, handleEncryptVM, 0 }, +#endif + { "mediumproperty", HELP_CMD_MEDIUMPROPERTY, handleMediumProperty, 0 }, + { "hdproperty", HELP_CMD_MEDIUMPROPERTY, handleMediumProperty, 0 }, /* backward compatibility */ + { "createmedium", HELP_CMD_CREATEMEDIUM, handleCreateMedium, 0 }, + { "createhd", HELP_CMD_CREATEMEDIUM, handleCreateMedium, 0 }, /* backward compatibility */ + { "createvdi", HELP_CMD_CREATEMEDIUM, handleCreateMedium, 0 }, /* backward compatibility */ + { "modifymedium", HELP_CMD_MODIFYMEDIUM, handleModifyMedium, 0 }, + { "modifyhd", HELP_CMD_MODIFYMEDIUM, handleModifyMedium, 0 }, /* backward compatibility */ + { "modifyvdi", HELP_CMD_MODIFYMEDIUM, handleModifyMedium, 0 }, /* backward compatibility */ + { "clonemedium", HELP_CMD_CLONEMEDIUM, handleCloneMedium, 0 }, + { "clonehd", HELP_CMD_CLONEMEDIUM, handleCloneMedium, 0 }, /* backward compatibility */ + { "clonevdi", HELP_CMD_CLONEMEDIUM, handleCloneMedium, 0 }, /* backward compatibility */ + { "encryptmedium", HELP_CMD_ENCRYPTMEDIUM, handleEncryptMedium, 0 }, + { "checkmediumpwd", HELP_CMD_CHECKMEDIUMPWD, handleCheckMediumPassword, 0 }, + { "createvm", HELP_CMD_CREATEVM, handleCreateVM, 0 }, + { "modifyvm", HELP_CMD_MODIFYVM, handleModifyVM, 0 }, + { "startvm", HELP_CMD_STARTVM, handleStartVM, 0 }, + { "controlvm", HELP_CMD_CONTROLVM, handleControlVM, 0 }, + { "unattended", HELP_CMD_UNATTENDED, handleUnattended, 0 }, + { "discardstate", HELP_CMD_DISCARDSTATE, handleDiscardState, 0 }, + { "adoptstate", HELP_CMD_ADOPTSTATE, handleAdoptState, 0 }, + { "snapshot", HELP_CMD_SNAPSHOT, handleSnapshot, 0 }, + { "closemedium", HELP_CMD_CLOSEMEDIUM, handleCloseMedium, 0 }, + { "storageattach", HELP_CMD_STORAGEATTACH, handleStorageAttach, 0 }, + { "storagectl", HELP_CMD_STORAGECTL, handleStorageController, 0 }, + { "showmediuminfo", HELP_CMD_SHOWMEDIUMINFO, handleShowMediumInfo, 0 }, + { "showhdinfo", HELP_CMD_SHOWMEDIUMINFO, handleShowMediumInfo, 0 }, /* backward compatibility */ + { "showvdiinfo", HELP_CMD_SHOWMEDIUMINFO, handleShowMediumInfo, 0 }, /* backward compatibility */ + { "mediumio", HELP_CMD_MEDIUMIO, handleMediumIO, 0 }, + { "getextradata", HELP_CMD_GETEXTRADATA, handleGetExtraData, 0 }, + { "setextradata", HELP_CMD_SETEXTRADATA, handleSetExtraData, 0 }, + { "setproperty", HELP_CMD_SETPROPERTY, handleSetProperty, 0 }, + { "usbfilter", HELP_CMD_USBFILTER, handleUSBFilter, 0 }, + { "sharedfolder", HELP_CMD_SHAREDFOLDER, handleSharedFolder, 0 }, +#ifdef VBOX_WITH_GUEST_PROPS + { "guestproperty", HELP_CMD_GUESTPROPERTY, handleGuestProperty, 0 }, +#endif +#ifdef VBOX_WITH_GUEST_CONTROL + { "guestcontrol", HELP_CMD_GUESTCONTROL, handleGuestControl, 0 }, +#endif + { "metrics", HELP_CMD_METRICS, handleMetrics, 0 }, + { "import", HELP_CMD_IMPORT, handleImportAppliance, 0 }, + { "export", HELP_CMD_EXPORT, handleExportAppliance, 0 }, + { "signova", HELP_CMD_SIGNOVA, handleSignAppliance, VBMG_CMD_F_NO_COM }, +#ifdef VBOX_WITH_NETFLT + { "hostonlyif", HELP_CMD_HOSTONLYIF, handleHostonlyIf, 0 }, +#endif +#ifdef VBOX_WITH_VMNET + { "hostonlynet", HELP_CMD_HOSTONLYNET, handleHostonlyNet, 0 }, +#endif + { "dhcpserver", HELP_CMD_DHCPSERVER, handleDHCPServer, 0 }, +#ifdef VBOX_WITH_NAT_SERVICE + { "natnetwork", HELP_CMD_NATNETWORK, handleNATNetwork, 0 }, +#endif + { "extpack", HELP_CMD_EXTPACK, handleExtPack, 0 }, + { "bandwidthctl", HELP_CMD_BANDWIDTHCTL, handleBandwidthControl, 0 }, + { "debugvm", HELP_CMD_DEBUGVM, handleDebugVM, 0 }, + { "convertfromraw", HELP_CMD_CONVERTFROMRAW, handleConvertFromRaw, VBMG_CMD_F_NO_COM }, + { "convertdd", HELP_CMD_CONVERTFROMRAW, handleConvertFromRaw, VBMG_CMD_F_NO_COM }, + { "usbdevsource", HELP_CMD_USBDEVSOURCE, handleUSBDevSource, 0 }, + { "cloudprofile", HELP_CMD_CLOUDPROFILE, handleCloudProfile, 0 }, + { "cloud", HELP_CMD_CLOUD, handleCloud, 0 }, +#ifdef VBOX_WITH_UPDATE_AGENT + { "updatecheck", HELP_CMD_UPDATECHECK, handleUpdateCheck, 0 }, +#endif + { "modifynvram", HELP_CMD_MODIFYNVRAM, handleModifyNvram, 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) RT_NOTHROW_DEF +{ + 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 progress, uint32_t fFlags) +{ + using namespace com; + HRESULT hrc; + + AssertReturn(progress.isNotNull(), E_FAIL); + + /* grandfather the old callers */ + if (g_fDetailedProgress) + fFlags = SHOW_PROGRESS_DETAILS; + + const bool fDetailed = RT_BOOL(fFlags & SHOW_PROGRESS_DETAILS); + const bool fQuiet = !RT_BOOL(fFlags & (SHOW_PROGRESS | SHOW_PROGRESS_DETAILS)); + + + 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; + hrc = progress->COMGETTER(OperationCount)(&cOperations); + if (FAILED(hrc)) + { + RTStrmPrintf(g_pStdErr, VBoxManage::tr("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 (fFlags & SHOW_PROGRESS_DESC) + { + com::Bstr bstrDescription; + hrc = progress->COMGETTER(Description(bstrDescription.asOutParam())); + if (FAILED(hrc)) + { + RTStrmPrintf(g_pStdErr, VBoxManage::tr("Failed to get progress description: %Rhrc\n"), hrc); + return hrc; + } + + const char *pcszDescSep; + if (fDetailed) /* multiline output */ + pcszDescSep = "\n"; + else /* continues on the same line */ + pcszDescSep = ": "; + + RTStrmPrintf(g_pStdErr, "%ls%s", bstrDescription.raw(), pcszDescSep); + RTStrmFlush(g_pStdErr); + } + + if (!fQuiet && !fDetailed) + { + 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 (fDetailed) + { + 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, VBoxManage::tr("(%u/%u) %ls %02u%% => %02u%% (%d s remaining)\n"), ulOperation + 1, cOperations, + bstrOperationDescription.raw(), ulCurrentOperationPercent, ulCurrentPercent, lSecsRem); + ulLastPercent = ulCurrentPercent; + ulLastOperationPercent = ulCurrentOperationPercent; + } + } + else if (!fQuiet) + { + /* 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)) + { + /* async operation completed successfully */ + if (SUCCEEDED(iRc)) + { + if (!fDetailed) + { + if (fFlags == SHOW_PROGRESS_DESC) + RTStrmPrintf(g_pStdErr, "ok\n"); + else if (!fQuiet) + RTStrmPrintf(g_pStdErr, "100%%\n"); + } + } + else if (g_fCanceled) + RTStrmPrintf(g_pStdErr, VBoxManage::tr("CANCELED\n")); + else + { + if (fDetailed) + RTStrmPrintf(g_pStdErr, VBoxManage::tr("Progress state: %Rhrc\n"), iRc); + else if (fFlags != SHOW_PROGRESS_NONE) + RTStrmPrintf(g_pStdErr, "%Rhrc\n", iRc); + } + hrc = iRc; + } + else + { + if (!fDetailed) + RTStrmPrintf(g_pStdErr, "\n"); + RTStrmPrintf(g_pStdErr, VBoxManage::tr("Progress object failure: %Rhrc\n"), hrc); + } + RTStrmFlush(g_pStdErr); + return hrc; +} + + +void setBuiltInHelpLanguage(const char *pszLang) +{ +#ifdef VBOX_WITH_VBOXMANAGE_NLS + if (pszLang == NULL || pszLang[0] == '\0' || (pszLang[0] == 'C' && pszLang[1] == '\0')) + pszLang = "en_US"; + + /* find language entry matching exactly pszLang */ + PCHELP_LANG_ENTRY_T pHelpLangEntry = NULL; + for (uint32_t i = 0; i < g_cHelpLangEntries; i++) + { + if (strcmp(g_aHelpLangEntries[i].pszLang, pszLang) == 0) + { + pHelpLangEntry = &g_aHelpLangEntries[i]; + break; + } + } + + /* find first entry containing language specified if pszLang contains only language */ + if (pHelpLangEntry == NULL) + { + size_t const cchLang = strlen(pszLang); + for (uint32_t i = 0; i < g_cHelpLangEntries; i++) + { + if ( cchLang < g_aHelpLangEntries[i].cchLang + && memcmp(g_aHelpLangEntries[i].pszLang, pszLang, cchLang) == 0) + { + pHelpLangEntry = &g_aHelpLangEntries[i]; + break; + } + } + } + + /* set to en_US (i.e. untranslated) if not found */ + if (pHelpLangEntry == NULL) + pHelpLangEntry = &g_aHelpLangEntries[0]; + + ASMAtomicWritePtr(&g_pHelpLangEntry, pHelpLangEntry); +#else + NOREF(pszLang); +#endif +} + + +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) + ATL::CComModule _Module; /* Required internally by ATL (constructor records instance in global variable). */ +#endif + +#ifdef VBOX_WITH_VBOXMANAGE_NLS + /* + * Initialize the translator and associated fun. + */ + util::InitAutoLockSystem(); + ComObjPtr ptrEventListner; + PTRCOMPONENT pTrComponent = NULL; + VirtualBoxTranslator *pTranslator = VirtualBoxTranslator::instance(); + if (pTranslator != NULL) + { + char szNlsPath[RTPATH_MAX]; + vrc = RTPathAppPrivateNoArch(szNlsPath, sizeof(szNlsPath)); + if (RT_SUCCESS(vrc)) + vrc = RTPathAppend(szNlsPath, sizeof(szNlsPath), "nls" RTPATH_SLASH_STR "VBoxManageNls"); + if (RT_SUCCESS(vrc)) + { + vrc = pTranslator->registerTranslation(szNlsPath, true, &pTrComponent); + if (RT_SUCCESS(vrc)) + { + vrc = pTranslator->i_loadLanguage(NULL); + if (RT_SUCCESS(vrc)) + { + com::Utf8Str strLang = pTranslator->language(); + setBuiltInHelpLanguage(strLang.c_str()); + } + else + RTMsgWarning("Load language failed: %Rrc\n", vrc); + } + else + RTMsgWarning("Register translation failed: %Rrc\n", vrc); + } + else + RTMsgWarning("Path constructing failed: %Rrc\n", vrc); + } +#endif + + /* + * Parse the global options + */ + bool fShowLogo = false; + bool fShowHelp = false; + int iCmd = 1; + int iCmdArg; + const char *pszSettingsPw = NULL; + const char *pszSettingsPwFile = NULL; + int cResponseFileArgs = 0; + char **papszResponseFileArgs = NULL; + char **papszNewArgv = NULL; + + 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(g_pStdOut); + return 0; + } + fShowLogo = true; + fShowHelp = true; + iCmd++; + continue; + } + + 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; + } + if (!strcmp(argv[i], "--dump-build-type")) + { + /* Print the build type, and do nothing else. (Used by ValKit to detect build type.) */ + RTPrintf("%s\n", RTBldCfgType()); + return 0; + } + + 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(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, VBoxManage::tr("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, VBoxManage::tr("No password file specified")); + pszSettingsPwFile = argv[i+1]; + iCmd += 2; + } + else if (argv[i][0] == '@') + { + if (papszResponseFileArgs) + return RTMsgErrorExitFailure(VBoxManage::tr("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(VBoxManage::tr("Error reading response file '%s': %Rrc"), &argv[i][1], vrc); + vrc = RTStrValidateEncoding(pszResponseFile); + if (RT_FAILURE(vrc)) + { + RTFileReadAllFree(pszResponseFile, cbResponseFile); + return RTMsgErrorExitFailure(VBoxManage::tr("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(VBoxManage::tr("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(VBoxManage::tr("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++; + } + else + break; + } + + iCmdArg = iCmd + 1; + + /* + * Show the logo and lookup the command and deal with fShowHelp = true. + */ + if (fShowLogo) + showLogo(g_pStdOut); + + PCVBMGCMD pCmd = lookupCommand(argv[iCmd]); + if (pCmd && pCmd->enmCmdHelp != VBMG_CMD_INTERNAL) + setCurrentCommand(pCmd->enmCmdHelp); + + if ( pCmd + && ( fShowHelp + || argc - iCmdArg == 0)) + { + if (pCmd->enmCmdHelp == VBMG_CMD_INTERNAL) + printUsageInternalCmds(g_pStdOut); + else if (fShowHelp) + printHelp(g_pStdOut); + else + printUsage(g_pStdOut); + return RTEXITCODE_FAILURE; /* error */ + } + if (!pCmd) + { + if (!strcmp(argv[iCmd], "commands")) + { + RTPrintf(VBoxManage::tr("commands:\n")); + for (unsigned i = 0; i < RT_ELEMENTS(g_aCommands); i++) + if ( i == 0 /* skip backwards compatibility entries */ + || (g_aCommands[i].enmCmdHelp != g_aCommands[i - 1].enmCmdHelp)) + RTPrintf(" %s\n", g_aCommands[i].pszCommand); + return RTEXITCODE_SUCCESS; + } + return errorSyntax(VBoxManage::tr("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, + VBoxManage::tr("Failed to initialize COM because the global settings directory '%s' is not accessible!"), szHome); + } +# endif + return RTMsgErrorExit(RTEXITCODE_FAILURE, VBoxManage::tr("Failed to initialize COM! (hrc=%Rhrc)"), hrc); + } + + + /* + * Get the remote VirtualBox object and create a local session object. + */ + rcExit = RTEXITCODE_FAILURE; + ComPtr virtualBoxClient; + ComPtr virtualBox; + hrc = virtualBoxClient.createInprocObject(CLSID_VirtualBoxClient); + if (SUCCEEDED(hrc)) + hrc = virtualBoxClient->COMGETTER(VirtualBox)(virtualBox.asOutParam()); + if (SUCCEEDED(hrc)) + { +#ifdef VBOX_WITH_VBOXMANAGE_NLS + /* Load language settings from IVirtualBox. */ + if (pTranslator != NULL) + { + HRESULT hrc1 = pTranslator->loadLanguage(virtualBox); + if (SUCCEEDED(hrc1)) + { + com::Utf8Str strLang = pTranslator->language(); + setBuiltInHelpLanguage(strLang.c_str()); + } + else + RTMsgWarning("Failed to load API language: %Rhrc", hrc1); + + /* VirtualBox language events registration. */ + ComPtr pES; + hrc1 = virtualBox->COMGETTER(EventSource)(pES.asOutParam()); + if (SUCCEEDED(hrc1)) + { + hrc1 = ptrEventListner.createObject(); + if (SUCCEEDED(hrc1)) + hrc1 = ptrEventListner->init(new VBoxEventListener()); + if (SUCCEEDED(hrc1)) + { + com::SafeArray eventTypes; + eventTypes.push_back(VBoxEventType_OnLanguageChanged); + hrc1 = pES->RegisterListener(ptrEventListner, ComSafeArrayAsInParam(eventTypes), true); + } + if (FAILED(hrc1)) + { + ptrEventListner.setNull(); + RTMsgWarning("Failed to register event listener: %Rhrc", hrc1); + } + } + } +#endif + + ComPtr 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(VBoxManage::tr("Failed to create a session object!")); + if (!info.isFullAvailable() && !info.isBasicAvailable()) + com::GluePrintRCMessage(hrc); + else + com::GluePrintErrorInfo(info); + } + } + else + { + com::ErrorInfo info; + RTMsgError(VBoxManage::tr("Failed to create the VirtualBox object!")); + if (!info.isFullAvailable() && !info.isBasicAvailable()) + { + com::GluePrintRCMessage(hrc); + RTMsgError(VBoxManage::tr("Most likely, the VirtualBox COM server is not running or failed to start.")); + } + else + com::GluePrintErrorInfo(info); + } + +#ifdef VBOX_WITH_VBOXMANAGE_NLS + /* VirtualBox event callback unregistration. */ + if (ptrEventListner.isNotNull()) + { + ComPtr pES; + HRESULT hrc1 = virtualBox->COMGETTER(EventSource)(pES.asOutParam()); + if (pES.isNotNull()) + { + hrc1 = pES->UnregisterListener(ptrEventListner); + if (FAILED(hrc1)) + LogRel(("Failed to unregister listener, %Rhrc", hrc1)); + } + ptrEventListner.setNull(); + } +#endif + /* + * 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); + } + +#ifdef VBOX_WITH_VBOXMANAGE_NLS + if (pTranslator != NULL) + { + pTranslator->release(); + pTranslator = NULL; + pTrComponent = NULL; + } +#endif + + if (papszResponseFileArgs) + { + RTGetOptArgvFree(papszResponseFileArgs); + RTMemFree(papszNewArgv); + } + + return rcExit; +} diff --git a/src/VBox/Frontends/VBoxManage/VBoxManage.h b/src/VBox/Frontends/VBoxManage/VBoxManage.h new file mode 100644 index 00000000..35ab4a15 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManage.h @@ -0,0 +1,313 @@ +/* $Id: VBoxManage.h $ */ +/** @file + * VBoxManage - VirtualBox command-line interface, internal header file. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#ifndef VBOX_INCLUDED_SRC_VBoxManage_VBoxManage_h +#define VBOX_INCLUDED_SRC_VBoxManage_VBoxManage_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "VBoxManageBuiltInHelp.h" +#include "PasswordInput.h" + +#ifdef VBOX_WITH_VBOXMANAGE_NLS +# include "VirtualBoxTranslator.h" +#endif + + +//////////////////////////////////////////////////////////////////////////////// +// +// definitions +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * This defines a a_CtxName::tr function that gives the translator context as + * well as providing a shorter way to call VirtualBoxTranslator::translate. + */ +#ifdef VBOX_WITH_VBOXMANAGE_NLS +# define DECLARE_TRANSLATION_CONTEXT(a_CtxName) \ +struct a_CtxName \ +{ \ + static const char *tr(const char *pszSource, const char *pszComment = NULL, const size_t uNum = ~(size_t)0) \ + { \ + return VirtualBoxTranslator::translate(NULL, #a_CtxName, pszSource, pszComment, uNum); \ + } \ +} +#else +# define DECLARE_TRANSLATION_CONTEXT(a_CtxName) \ +struct a_CtxName \ +{ \ + static const char *tr(const char *pszSource, const char *pszComment = NULL, const size_t uNum = ~(size_t)0) \ + { \ + RT_NOREF(pszComment, uNum); \ + return pszSource; \ + } \ +} +#endif + +/** + * Defines an option with two variants, producing two RTGETOPTDEF entries. + * + * This is mainly for replacing character-soup option names like + * --natlocalhostreachable and --biossystemtimeoffset with more easily parsed + * ones, like --nat-localhost-reachable and --bios-system-time-offset, without + * removing the legacy name. + */ +#define OPT2(a_pszWordDashWord, a_pszWordSoup, a_chOptOrValue, a_fFlags) \ + { a_pszWordDashWord, a_chOptOrValue, a_fFlags }, \ + { a_pszWordSoup, a_chOptOrValue, a_fFlags } + +/** A single option variant of OPT2 for better looking tables. */ +#define OPT1(a_pszOption, a_chOptOrValue, a_fFlags) \ + { a_pszOption, a_chOptOrValue, a_fFlags } + + +/** command handler argument */ +struct HandlerArg +{ + int argc; + char **argv; + + ComPtr virtualBox; + ComPtr session; +}; + + +/** 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 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 errorFetchValue(int iValueNo, const char *pszOption, int rcGetOptFetchValue, union RTGETOPTUNION const *pValueUnion); +RTEXITCODE errorSyntax(const char *pszFormat, ...) RT_IPRT_FORMAT_ATTR(1, 2); +RTEXITCODE errorSyntaxV(const char *pszFormat, va_list va) RT_IPRT_FORMAT_ATTR(1, 0); +HRESULT errorSyntaxHr(const char *pszFormat, ...) RT_IPRT_FORMAT_ATTR(1, 2); +RTEXITCODE errorArgument(const char *pszFormat, ...) RT_IPRT_FORMAT_ATTR(1, 2); +HRESULT errorArgumentHr(const char *pszFormat, ...) RT_IPRT_FORMAT_ATTR(1, 2); + +# define SHOW_PROGRESS_NONE 0 +# define SHOW_PROGRESS_DESC RT_BIT_32(0) +# define SHOW_PROGRESS RT_BIT_32(1) +# define SHOW_PROGRESS_DETAILS RT_BIT_32(2) +HRESULT showProgress(ComPtr progress, uint32_t fFlags = SHOW_PROGRESS); + +/* VBoxManage.cpp */ +void showLogo(PRTSTREAM pStrm); + +/* VBoxInternalManage.cpp */ +DECLHIDDEN(void) printUsageInternalCmds(PRTSTREAM pStrm); +RTEXITCODE handleInternalCommands(HandlerArg *a); + +/* VBoxManageControlVM.cpp */ +RTEXITCODE handleControlVM(HandlerArg *a); + +/* VBoxManageModifyVM.cpp */ +void parseGroups(const char *pcszGroups, com::SafeArray *pGroups); +#ifdef VBOX_WITH_RECORDING +int parseScreens(const char *pcszScreens, com::SafeArray *pScreens); +#endif +RTEXITCODE handleModifyVM(HandlerArg *a); + +/* VBoxManageDebugVM.cpp */ +RTEXITCODE handleDebugVM(HandlerArg *a); + +/* VBoxManageGuestProp.cpp */ +RTEXITCODE handleGuestProperty(HandlerArg *a); + +/* VBoxManageGuestCtrl.cpp */ +RTEXITCODE handleGuestControl(HandlerArg *a); + +/* VBoxManageVMInfo.cpp */ +HRESULT showSnapshots(ComPtr &rootSnapshot, + ComPtr ¤tSnapshot, + VMINFO_DETAILS details, + const com::Utf8Str &prefix = "", + int level = 0); +RTEXITCODE handleShowVMInfo(HandlerArg *a); +HRESULT showVMInfo(ComPtr pVirtualBox, + ComPtr pMachine, + ComPtr pSession, + VMINFO_DETAILS details = VMINFO_NONE); +const char *machineStateToName(MachineState_T machineState, bool fShort); +HRESULT showBandwidthGroups(ComPtr &bwCtrl, + VMINFO_DETAILS details); +void outputMachineReadableString(const char *pszName, const char *pszValue, bool fQuoteName = false, bool fNewline = true); +void outputMachineReadableString(const char *pszName, com::Bstr const *pbstrValue, bool fQuoteName = false, bool fNewline = true); +void outputMachineReadableStringWithFmtName(const char *pszValue, bool fQuoteName, const char *pszNameFmt, ...) RT_IPRT_FORMAT_ATTR(3, 4); +void outputMachineReadableStringWithFmtName(com::Bstr const *pbstrValue, bool fQuoteName, const char *pszNameFmt, ...) RT_IPRT_FORMAT_ATTR(3, 4); +void outputMachineReadableBool(const char *pszName, BOOL const *pfValue); +void outputMachineReadableBool(const char *pszName, bool const *pfValue); +void outputMachineReadableULong(const char *pszName, ULONG *uValue); +void outputMachineReadableLong64(const char *pszName, LONG64 *uValue); + +/* 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); +#ifdef VBOX_WITH_FULL_VM_ENCRYPTION +RTEXITCODE handleEncryptVM(HandlerArg *a); +#endif +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); +RTEXITCODE handleCloudProfile(HandlerArg *a); + +/* VBoxManageDisk.cpp */ +HRESULT openMedium(HandlerArg *a, const char *pszFilenameOrUuid, + DeviceType_T enmDevType, AccessMode_T enmAccessMode, + ComPtr &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 &pVirtualBox, + const ComPtr &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); + +// VBoxManageAppliance.cpp +RTEXITCODE handleImportAppliance(HandlerArg *a); +RTEXITCODE handleExportAppliance(HandlerArg *a); +RTEXITCODE handleSignAppliance(HandlerArg *a); + +// VBoxManageSnapshot.cpp +RTEXITCODE handleSnapshot(HandlerArg *a); + +/* VBoxManageUSB.cpp */ +RTEXITCODE handleUSBFilter(HandlerArg *a); +RTEXITCODE handleUSBDevSource(HandlerArg *a); + +/* VBoxManageHostonly.cpp */ +RTEXITCODE handleHostonlyIf(HandlerArg *a); +#ifdef VBOX_WITH_VMNET +RTEXITCODE handleHostonlyNet(HandlerArg *a); +#endif /* VBOX_WITH_VMNET */ + +/* VBoxManageDHCPServer.cpp */ +RTEXITCODE handleDHCPServer(HandlerArg *a); + +/* VBoxManageNATNetwork.cpp */ +RTEXITCODE handleNATNetwork(HandlerArg *a); +RTEXITCODE listNATNetworks(bool fLong, bool fSorted, + const ComPtr &pVirtualBox); + +/* VBoxManageBandwidthControl.cpp */ +RTEXITCODE handleBandwidthControl(HandlerArg *a); + +/* VBoxManageCloud.cpp */ +RTEXITCODE handleCloud(HandlerArg *a); + +/* VBoxManageCloudMachine.cpp */ +RTEXITCODE handleCloudMachine(HandlerArg *a, int iFirst, + const char *pcszProviderName, + const char *pcszProfileName); +RTEXITCODE listCloudMachines(HandlerArg *a, int iFirst, + const char *pcszProviderName, + const char *pcszProfileName); +RTEXITCODE handleCloudShowVMInfo(HandlerArg *a, int iFirst, + const char *pcszProviderName, + const char *pcszProfileName); + +#ifdef VBOX_WITH_UPDATE_AGENT +/* VBoxManageUpdateCheck.cpp */ +RTEXITCODE handleUpdateCheck(HandlerArg *a); +#endif + +/* VBoxManageModifyNvram.cpp */ +RTEXITCODE handleModifyNvram(HandlerArg *a); + +#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..e2aa3172 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManage.rc @@ -0,0 +1,61 @@ +/* $Id: VBoxManage.rc $ */ +/** @file + * VBoxManage - Resource file containing version info and icon. + */ + +/* + * Copyright (C) 2015-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include +#include + +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..fb44a061 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageAppliance.cpp @@ -0,0 +1,2916 @@ +/* $Id: VBoxManageAppliance.cpp $ */ +/** @file + * VBoxManage - The appliance-related commands. + */ + +/* + * Copyright (C) 2009-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#include "VBoxManage.h" +using namespace com; + +DECLARE_TRANSLATION_CONTEXT(Appliance); + + +// funcs +/////////////////////////////////////////////////////////////////////////////// + +typedef std::map ArgsMap; // pairs of strings like "vmname" => "newvmname" +typedef std::map ArgsMapsMap; // map of maps, one for each virtual system, sorted by index + +typedef std::map IgnoresMap; // pairs of numeric description entry indices +typedef std::map 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 *options) +{ + int vrc = VINF_SUCCESS; + while (psz && *psz && RT_SUCCESS(vrc)) + { + 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 + vrc = VERR_PARSE_ERROR; + } + if (pszComma) + psz += len + 1; + else + psz += len; + } + + return vrc; +} + +/** + * Helper routine to parse the ExtraData Utf8Str for a storage controller's + * value or channel value. + * + * @param aExtraData The ExtraData string which can have a format of + * either 'controller=13;channel=3' or '11'. + * @param pszKey The string being looked up, usually either 'controller' + * or 'channel' but can be NULL or empty. + * @param puVal The integer value of the 'controller=' or 'channel=' + * key (or the controller number when there is no key) in + * the ExtraData string. + * @returns COM status code. + */ +static int getStorageControllerDetailsFromStr(const com::Utf8Str &aExtraData, const char *pszKey, uint32_t *puVal) +{ + int vrc; + + if (pszKey && *pszKey) + { + size_t posKey = aExtraData.find(pszKey); + if (posKey == Utf8Str::npos) + return VERR_INVALID_PARAMETER; + vrc = RTStrToUInt32Ex(aExtraData.c_str() + posKey + strlen(pszKey), NULL, 0, puVal); + } + else + { + vrc = RTStrToUInt32Ex(aExtraData.c_str(), NULL, 0, puVal); + } + + if (vrc == VWRN_NUMBER_TOO_BIG || vrc == VWRN_NEGATIVE_UNSIGNED) + return VERR_INVALID_PARAMETER; + + return vrc; +} + +static bool isStorageControllerType(VirtualSystemDescriptionType_T avsdType) +{ + switch (avsdType) + { + case VirtualSystemDescriptionType_HardDiskControllerIDE: + case VirtualSystemDescriptionType_HardDiskControllerSATA: + case VirtualSystemDescriptionType_HardDiskControllerSCSI: + case VirtualSystemDescriptionType_HardDiskControllerSAS: + case VirtualSystemDescriptionType_HardDiskControllerVirtioSCSI: + return true; + default: + return false; + } +} + +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 + { "--controller", 'C', RTGETOPT_REQ_STRING }, + { "--port", 'E', RTGETOPT_REQ_STRING }, + { "--disk", 'D', RTGETOPT_REQ_STRING }, + { "--options", 'O', RTGETOPT_REQ_STRING }, + + { "--cloud", 'j', RTGETOPT_REQ_NOTHING}, + { "--cloudprofile", 'k', RTGETOPT_REQ_STRING }, + { "--cloudinstanceid", 'l', RTGETOPT_REQ_STRING }, + { "--cloudbucket", 'B', RTGETOPT_REQ_STRING } +}; + +typedef enum APPLIANCETYPE +{ + NOT_SET, LOCAL, CLOUD +} APPLIANCETYPE; + +RTEXITCODE handleImportAppliance(HandlerArg *arg) +{ + HRESULT hrc = S_OK; + APPLIANCETYPE enmApplType = NOT_SET; + Utf8Str strOvfFilename; + bool fExecute = true; // if true, then we actually do the import + com::SafeArray 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 + if (enmApplType == NOT_SET) + enmApplType = LOCAL; + + if (enmApplType != LOCAL) + return errorSyntax(Appliance::tr("Option \"%s\" can't be used together with \"--cloud\" option."), + GetState.pDef->pszLong); + if (ValueUnion.u32 == (uint32_t)-1) + return errorSyntax(Appliance::tr("Value of option \"%s\" is out of range."), + GetState.pDef->pszLong); + + ulCurVsys = ValueUnion.u32; + ulCurUnit = (uint32_t)-1; + break; + + case 'o': // --ostype + if (enmApplType == NOT_SET) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys or --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["ostype"] = ValueUnion.psz; + break; + + case 'V': // --vmname + if (enmApplType == NOT_SET) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys or --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["vmname"] = ValueUnion.psz; + break; + + case 'S': // --settingsfile + if (enmApplType != LOCAL) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["settingsfile"] = ValueUnion.psz; + break; + + case 'p': // --basefolder + if (enmApplType == NOT_SET) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys or --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["basefolder"] = ValueUnion.psz; + break; + + case 'g': // --group + if (enmApplType != LOCAL) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["group"] = ValueUnion.psz; + break; + + case 'd': // --description + if (enmApplType == NOT_SET) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys or --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["description"] = ValueUnion.psz; + break; + + case 'L': // --eula + if (enmApplType != LOCAL) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["eula"] = ValueUnion.psz; + break; + + case 'm': // --memory + if (enmApplType == NOT_SET) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys or --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["memory"] = ValueUnion.psz; + break; + + case 'c': // --cpus + if (enmApplType == NOT_SET) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys or --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["cpus"] = ValueUnion.psz; + break; + + case 'u': // --unit + if (enmApplType != LOCAL) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."), + GetState.pDef->pszLong); + if (ValueUnion.u32 == (uint32_t)-1) + return errorSyntax(Appliance::tr("Value of option \"%s\" is out of range."), + GetState.pDef->pszLong); + + ulCurUnit = ValueUnion.u32; + break; + + case 'x': // --ignore + if (enmApplType != LOCAL) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."), + GetState.pDef->pszLong); + if (ulCurUnit == (uint32_t)-1) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --unit option."), + GetState.pDef->pszLong); + mapIgnoresMapsPerVsys[ulCurVsys][ulCurUnit] = true; + break; + + case 'T': // --scsitype + if (enmApplType != LOCAL) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."), + GetState.pDef->pszLong); + if (ulCurUnit == (uint32_t)-1) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --unit option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("scsitype%u", ulCurUnit)] = ValueUnion.psz; + break; + + case 'C': // --controller + if (enmApplType != LOCAL) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."), + GetState.pDef->pszLong); + if (ulCurUnit == (uint32_t)-1) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --unit option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("controller%u", ulCurUnit)] = ValueUnion.psz; + break; + + case 'E': // --port + if (enmApplType != LOCAL) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."), + GetState.pDef->pszLong); + if (ulCurUnit == (uint32_t)-1) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --unit option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("port%u", ulCurUnit)] = ValueUnion.psz; + break; + + case 'D': // --disk + if (enmApplType != LOCAL) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."), + GetState.pDef->pszLong); + if (ulCurUnit == (uint32_t)-1) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --unit option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("disk%u", ulCurUnit)] = ValueUnion.psz; + break; + + case 'O': // --options + if (RT_FAILURE(parseImportOptions(ValueUnion.psz, &options))) + return errorArgument(Appliance::tr("Invalid import options '%s'\n"), ValueUnion.psz); + break; + + /*--cloud and --vsys are orthogonal, only one must be presented*/ + case 'j': // --cloud + if (enmApplType == NOT_SET) + enmApplType = CLOUD; + + if (enmApplType != CLOUD) + return errorSyntax(Appliance::tr("Option \"%s\" can't be used together with \"--vsys\" option."), + GetState.pDef->pszLong); + + ulCurVsys = 0; + break; + + /* Cloud export settings */ + case 'k': // --cloudprofile + if (enmApplType != CLOUD) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["cloudprofile"] = ValueUnion.psz; + break; + + case 'l': // --cloudinstanceid + if (enmApplType != CLOUD) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["cloudinstanceid"] = ValueUnion.psz; + break; + + case 'B': // --cloudbucket + if (enmApplType != CLOUD) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["cloudbucket"] = ValueUnion.psz; + break; + + case VINF_GETOPT_NOT_OPTION: + if (strOvfFilename.isEmpty()) + strOvfFilename = ValueUnion.psz; + else + return errorSyntax(Appliance::tr("Invalid parameter '%s'"), ValueUnion.psz); + break; + + default: + if (c > 0) + { + if (RT_C_IS_PRINT(c)) + return errorSyntax(Appliance::tr("Invalid option -%c"), c); + else + return errorSyntax(Appliance::tr("Invalid option case %i"), c); + } + else if (c == VERR_GETOPT_UNKNOWN_OPTION) + return errorSyntax(Appliance::tr("unknown option: %s\n"), ValueUnion.psz); + else if (ValueUnion.pDef) + return errorSyntax("%s: %Rrs", ValueUnion.pDef->pszLong, c); + else + return errorSyntax(Appliance::tr("error: %Rrs"), c); + } + } + + /* Last check after parsing all arguments */ + if (strOvfFilename.isEmpty()) + return errorSyntax(Appliance::tr("Not enough arguments for \"import\" command.")); + + if (enmApplType == NOT_SET) + enmApplType = LOCAL; + + do + { + ComPtr pAppliance; + CHECK_ERROR_BREAK(arg->virtualBox, CreateAppliance(pAppliance.asOutParam())); + //in the case of Cloud, append the instance id here because later it's harder to do + if (enmApplType == CLOUD) + { + try + { + /* Check presence of cloudprofile and cloudinstanceid in the map. + * If there isn't the exception is triggered. It's standard std:map logic.*/ + ArgsMap a = mapArgsMapsPerVsys[ulCurVsys]; + (void)a.at("cloudprofile"); + (void)a.at("cloudinstanceid"); + } + catch (...) + { + return errorSyntax(Appliance::tr("Not enough arguments for import from the Cloud.")); + } + + strOvfFilename.append(mapArgsMapsPerVsys[ulCurVsys]["cloudprofile"]); + strOvfFilename.append("/"); + strOvfFilename.append(mapArgsMapsPerVsys[ulCurVsys]["cloudinstanceid"]); + } + + char *pszAbsFilePath; + if (strOvfFilename.startsWith("S3://", RTCString::CaseInsensitive) || + strOvfFilename.startsWith("SunCloud://", RTCString::CaseInsensitive) || + strOvfFilename.startsWith("webdav://", RTCString::CaseInsensitive) || + strOvfFilename.startsWith("OCI://", RTCString::CaseInsensitive)) + pszAbsFilePath = RTStrDup(strOvfFilename.c_str()); + else + pszAbsFilePath = RTPathAbsDup(strOvfFilename.c_str()); + + ComPtr progressRead; + CHECK_ERROR_BREAK(pAppliance, Read(Bstr(pszAbsFilePath).raw(), + progressRead.asOutParam())); + RTStrFree(pszAbsFilePath); + + hrc = showProgress(progressRead); + CHECK_PROGRESS_ERROR_RET(progressRead, (Appliance::tr("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())); + + size_t cVirtualSystemDescriptions = 0; + com::SafeIfaceArray aVirtualSystemDescriptions; + + if (enmApplType == LOCAL) + { + // call interpret(); this can yield both warnings and errors, so we need + // to tinker with the error info a bit + RTStrmPrintf(g_pStdErr, Appliance::tr("Interpreting %ls...\n"), path.raw()); + hrc = pAppliance->Interpret(); + com::ErrorInfoKeeper eik; + + /** @todo r=klaus Eliminate this special way of signalling + * warnings which should be part of the ErrorInfo. */ + com::SafeArray 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()); + } + } + + eik.restore(); + if (FAILED(hrc)) // during interpret, after printing warnings + { + com::GlueHandleComError(pAppliance, "Interpret()", hrc, __FILE__, __LINE__); + break; + } + + RTStrmPrintf(g_pStdErr, "OK.\n"); + + // fetch all disks + com::SafeArray retDisks; + CHECK_ERROR_BREAK(pAppliance, + COMGETTER(Disks)(ComSafeArrayAsOutParam(retDisks))); + if (retDisks.size() > 0) + { + RTPrintf(Appliance::tr("Disks:\n")); + for (unsigned i = 0; i < retDisks.size(); i++) + RTPrintf(" %ls\n", retDisks[i]); + RTPrintf("\n"); + } + + // fetch virtual system descriptions + CHECK_ERROR_BREAK(pAppliance, + COMGETTER(VirtualSystemDescriptions)(ComSafeArrayAsOutParam(aVirtualSystemDescriptions))); + + 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(Appliance::tr("Invalid index %RI32 with -vsys option; the OVF contains only %zu virtual system(s).", + "", cVirtualSystemDescriptions), + ulVsys, cVirtualSystemDescriptions); + } + } + else if (enmApplType == CLOUD) + { + /* In the Cloud case the call of interpret() isn't needed because there isn't any OVF XML file. + * All info is got from the Cloud and VSD is filled inside IAppliance::read(). */ + // fetch virtual system descriptions + CHECK_ERROR_BREAK(pAppliance, + COMGETTER(VirtualSystemDescriptions)(ComSafeArrayAsOutParam(aVirtualSystemDescriptions))); + + cVirtualSystemDescriptions = aVirtualSystemDescriptions.size(); + } + + 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 retTypes; + com::SafeArray aRefs; + com::SafeArray aOvfValues; + com::SafeArray aVBoxValues; + com::SafeArray aExtraConfigValues; + CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i], + GetDescription(ComSafeArrayAsOutParam(retTypes), + ComSafeArrayAsOutParam(aRefs), + ComSafeArrayAsOutParam(aOvfValues), + ComSafeArrayAsOutParam(aVBoxValues), + ComSafeArrayAsOutParam(aExtraConfigValues))); + + RTPrintf(Appliance::tr("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 aEnabled(retTypes.size()); + com::SafeArray 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(Appliance::tr("%2u: OS type specified with --ostype: \"%ls\"\n"), + a, bstrFinalValue.raw()); + } + else + RTPrintf(Appliance::tr("%2u: Suggested OS type: \"%ls\"\n" + " (change with \"--vsys %u --ostype \"; 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(Appliance::tr("%2u: VM name specified with --vmname: \"%ls\"\n"), + a, bstrFinalValue.raw()); + } + else + RTPrintf(Appliance::tr("%2u: Suggested VM name \"%ls\"\n" + " (change with \"--vsys %u --vmname \")\n"), + a, bstrFinalValue.raw(), i); + break; + + case VirtualSystemDescriptionType_Product: + RTPrintf(Appliance::tr("%2u: Product (ignored): %ls\n"), + a, aVBoxValues[a]); + break; + + case VirtualSystemDescriptionType_ProductUrl: + RTPrintf(Appliance::tr("%2u: ProductUrl (ignored): %ls\n"), + a, aVBoxValues[a]); + break; + + case VirtualSystemDescriptionType_Vendor: + RTPrintf(Appliance::tr("%2u: Vendor (ignored): %ls\n"), + a, aVBoxValues[a]); + break; + + case VirtualSystemDescriptionType_VendorUrl: + RTPrintf(Appliance::tr("%2u: VendorUrl (ignored): %ls\n"), + a, aVBoxValues[a]); + break; + + case VirtualSystemDescriptionType_Version: + RTPrintf(Appliance::tr("%2u: Version (ignored): %ls\n"), + a, aVBoxValues[a]); + break; + + case VirtualSystemDescriptionType_Description: + if (findArgValue(strOverride, pmapArgs, "description")) + { + bstrFinalValue = strOverride; + RTPrintf(Appliance::tr("%2u: Description specified with --description: \"%ls\"\n"), + a, bstrFinalValue.raw()); + } + else + RTPrintf(Appliance::tr("%2u: Description \"%ls\"\n" + " (change with \"--vsys %u --description \")\n"), + a, bstrFinalValue.raw(), i); + break; + + case VirtualSystemDescriptionType_License: + ++cLicensesInTheWay; + if (findArgValue(strOverride, pmapArgs, "eula")) + { + if (strOverride == "show") + { + RTPrintf(Appliance::tr("%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(Appliance::tr("%2u: End-user license agreement (accepted)\n"), + a); + --cLicensesInTheWay; + } + else + return errorSyntax(Appliance::tr("Argument to --eula must be either \"show\" or \"accept\".")); + } + else + RTPrintf(Appliance::tr("%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(Appliance::tr("%2u: No. of CPUs specified with --cpus: %ls\n"), + a, bstrFinalValue.raw()); + } + else + return errorSyntax(Appliance::tr("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(Appliance::tr("%2u: Number of CPUs: %ls\n (change with \"--vsys %u --cpus \")\n"), + a, bstrFinalValue.raw(), i); + break; + + case VirtualSystemDescriptionType_Memory: + { + if (findArgValue(strOverride, pmapArgs, "memory")) + { + uint32_t ulMemMB; + if (VINF_SUCCESS == strOverride.toInt(ulMemMB)) + { + /* 'VBoxManage import --memory' size is in megabytes */ + RTPrintf(Appliance::tr("%2u: Guest memory specified with --memory: %RU32 MB\n"), + a, ulMemMB); + + /* IVirtualSystemDescription guest memory size is in bytes. + It's always stored in bytes in VSD according to the old internal agreement within the team */ + uint64_t ullMemBytes = (uint64_t)ulMemMB * _1M; + strOverride = Utf8StrFmt("%RU64", ullMemBytes); + bstrFinalValue = strOverride; + } + else + return errorSyntax(Appliance::tr("Argument to --memory option must be a non-negative number.")); + } + else + { + strOverride = aVBoxValues[a]; + uint64_t ullMemMB = strOverride.toUInt64() / _1M; + RTPrintf(Appliance::tr("%2u: Guest memory: %RU64 MB\n (change with \"--vsys %u --memory \")\n"), + a, ullMemMB, i); + } + break; + } + + case VirtualSystemDescriptionType_HardDiskControllerIDE: + if (fIgnoreThis) + { + RTPrintf(Appliance::tr("%2u: IDE controller, type %ls -- disabled\n"), + a, + aVBoxValues[a]); + aEnabled[a] = false; + } + else + RTPrintf(Appliance::tr("%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(Appliance::tr("%2u: SATA controller, type %ls -- disabled\n"), + a, + aVBoxValues[a]); + aEnabled[a] = false; + } + else + RTPrintf(Appliance::tr("%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(Appliance::tr("%2u: SAS controller, type %ls -- disabled\n"), + a, + aVBoxValues[a]); + aEnabled[a] = false; + } + else + RTPrintf(Appliance::tr("%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(Appliance::tr("%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(Appliance::tr("%2u: SCSI controller, type set with --unit %u --scsitype: \"%ls\"\n"), + a, + a, + bstrFinalValue.raw()); + } + else + RTPrintf(Appliance::tr("%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_HardDiskControllerVirtioSCSI: + if (fIgnoreThis) + { + RTPrintf(Appliance::tr("%2u: VirtioSCSI controller, type %ls -- disabled\n"), + a, + aVBoxValues[a]); + aEnabled[a] = false; + } + else + RTPrintf(Appliance::tr("%2u: VirtioSCSI controller, type %ls\n" + " (disable with \"--vsys %u --unit %u --ignore\")\n"), + a, + aVBoxValues[a], + i, a); + break; + + case VirtualSystemDescriptionType_HardDiskControllerNVMe: + if (fIgnoreThis) + { + RTPrintf(Appliance::tr("%2u: NVMe controller, type %ls -- disabled\n"), + a, + aVBoxValues[a]); + aEnabled[a] = false; + } + else + RTPrintf(Appliance::tr("%2u: NVMe controller, type %ls\n" + " (disable with \"--vsys %u --unit %u --ignore\")\n"), + a, + aVBoxValues[a], + i, a); + break; + + case VirtualSystemDescriptionType_HardDiskImage: + if (fIgnoreThis) + { + RTPrintf(Appliance::tr("%2u: Hard disk image: source image=%ls -- disabled\n"), + a, + aOvfValues[a]); + aEnabled[a] = false; + } + else + { + Utf8StrFmt strTypeArg("disk%u", a); + bool fDiskChanged = false; + int vrc; + RTCList optionsList = options.toList(); + + if (findArgValue(strOverride, pmapArgs, strTypeArg)) + { + if (optionsList.contains(ImportOptions_ImportToVDI)) + return errorSyntax(Appliance::tr("Option --ImportToVDI can not be used together with a manually set target path.")); + RTUUID uuid; + /* Check if this is a uuid. If so, don't touch. */ + 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; + fDiskChanged = true; + } + + strTypeArg.printf("controller%u", a); + bool fControllerChanged = false; + uint32_t uTargetController = (uint32_t)-1; + VirtualSystemDescriptionType_T vsdControllerType = VirtualSystemDescriptionType_Ignore; + Utf8Str strExtraConfigValue; + if (findArgValue(strOverride, pmapArgs, strTypeArg)) + { + vrc = getStorageControllerDetailsFromStr(strOverride, NULL, &uTargetController); + if (RT_FAILURE(vrc)) + return errorSyntax(Appliance::tr("Invalid controller value: '%s'"), + strOverride.c_str()); + + vsdControllerType = retTypes[uTargetController]; + if (!isStorageControllerType(vsdControllerType)) + return errorSyntax(Appliance::tr("Invalid storage controller specified: %u"), + uTargetController); + + fControllerChanged = true; + } + + strTypeArg.printf("port%u", a); + bool fControllerPortChanged = false; + uint32_t uTargetControllerPort = (uint32_t)-1;; + if (findArgValue(strOverride, pmapArgs, strTypeArg)) + { + vrc = getStorageControllerDetailsFromStr(strOverride, NULL, &uTargetControllerPort); + if (RT_FAILURE(vrc)) + return errorSyntax(Appliance::tr("Invalid port value: '%s'"), + strOverride.c_str()); + + fControllerPortChanged = true; + } + + /* + * aExtraConfigValues[a] has a format of 'controller=12;channel=0' and is set by + * Appliance::interpret() so any parsing errors here aren't due to user-supplied + * values so different error messages here. + */ + uint32_t uOrigController; + Utf8Str strOrigController(Bstr(aExtraConfigValues[a]).raw()); + vrc = getStorageControllerDetailsFromStr(strOrigController, "controller=", &uOrigController); + if (RT_FAILURE(vrc)) + return RTMsgErrorExitFailure(Appliance::tr("Failed to extract controller value from ExtraConfig: '%s'"), + strOrigController.c_str()); + + uint32_t uOrigControllerPort; + vrc = getStorageControllerDetailsFromStr(strOrigController, "channel=", &uOrigControllerPort); + if (RT_FAILURE(vrc)) + return RTMsgErrorExitFailure(Appliance::tr("Failed to extract channel value from ExtraConfig: '%s'"), + strOrigController.c_str()); + + /* + * The 'strExtraConfigValue' string is used to display the storage controller and + * port details for each virtual hard disk using the more accurate 'controller=' and + * 'port=' labels. The aExtraConfigValues[a] string has a format of + * 'controller=%u;channel=%u' from Appliance::interpret() which is required as per + * the API but for consistency and clarity with the CLI options --controller and + * --port we instead use strExtraConfigValue in the output below. + */ + strExtraConfigValue = Utf8StrFmt("controller=%u;port=%u", uOrigController, uOrigControllerPort); + + if (fControllerChanged || fControllerPortChanged) + { + /* + * Verify that the new combination of controller and controller port is valid. + * cf. StorageController::i_checkPortAndDeviceValid() + */ + if (uTargetControllerPort == (uint32_t)-1) + uTargetControllerPort = uOrigControllerPort; + if (uTargetController == (uint32_t)-1) + uTargetController = uOrigController; + + if ( uOrigController == uTargetController + && uOrigControllerPort == uTargetControllerPort) + return errorSyntax(Appliance::tr("Device already attached to controller %u at this port (%u) location."), + uTargetController, + uTargetControllerPort); + + if (vsdControllerType == VirtualSystemDescriptionType_Ignore) + vsdControllerType = retTypes[uOrigController]; + if (!isStorageControllerType(vsdControllerType)) + return errorSyntax(Appliance::tr("Invalid storage controller specified: %u"), + uOrigController); + + ComPtr pVirtualBox = arg->virtualBox; + ComPtr systemProperties; + CHECK_ERROR(pVirtualBox, COMGETTER(SystemProperties)(systemProperties.asOutParam())); + ULONG maxPorts = 0; + StorageBus_T enmStorageBus = StorageBus_Null;; + switch (vsdControllerType) + { + case VirtualSystemDescriptionType_HardDiskControllerIDE: + enmStorageBus = StorageBus_IDE; + break; + case VirtualSystemDescriptionType_HardDiskControllerSATA: + enmStorageBus = StorageBus_SATA; + break; + case VirtualSystemDescriptionType_HardDiskControllerSCSI: + enmStorageBus = StorageBus_SCSI; + break; + case VirtualSystemDescriptionType_HardDiskControllerSAS: + enmStorageBus = StorageBus_SAS; + break; + case VirtualSystemDescriptionType_HardDiskControllerVirtioSCSI: + enmStorageBus = StorageBus_VirtioSCSI; + break; + default: // Not reached since vsdControllerType validated above but silence gcc. + break; + } + CHECK_ERROR_RET(systemProperties, GetMaxPortCountForStorageBus(enmStorageBus, &maxPorts), + RTEXITCODE_FAILURE); + if (uTargetControllerPort >= maxPorts) + return errorSyntax(Appliance::tr("Illegal port value: %u. For %ls controllers the only valid values are 0 to %lu (inclusive)"), + uTargetControllerPort, + aVBoxValues[uTargetController], + maxPorts); + + /* + * The 'strOverride' string will be mapped to the strExtraConfigCurrent value in + * VirtualSystemDescription::setFinalValues() which is then used in the appliance + * import routines i_importVBoxMachine()/i_importMachineGeneric() later. This + * aExtraConfigValues[] array entry must have a format of + * 'controller=;channel=' as per the API documentation. + */ + strExtraConfigValue = Utf8StrFmt("controller=%u;port=%u", uTargetController, + uTargetControllerPort); + strOverride = Utf8StrFmt("controller=%u;channel=%u", uTargetController, + uTargetControllerPort); + Bstr bstrExtraConfigValue = strOverride; + bstrExtraConfigValue.detachTo(&aExtraConfigValues[a]); + } + + if (fDiskChanged && !fControllerChanged && !fControllerPortChanged) + { + RTPrintf(Appliance::tr("%2u: Hard disk image specified with --disk: source image=%ls, target path=%ls, %s\n" + " (change controller with \"--vsys %u --unit %u --controller \";\n" + " change controller port with \"--vsys %u --unit %u --port \")\n"), + a, + aOvfValues[a], + bstrFinalValue.raw(), + strExtraConfigValue.c_str(), + i, a, + i, a); + } + else if (fDiskChanged && fControllerChanged && !fControllerPortChanged) + { + RTPrintf(Appliance::tr("%2u: Hard disk image specified with --disk and --controller: source image=%ls, target path=%ls, %s\n" + " (change controller port with \"--vsys %u --unit %u --port \")\n"), + a, + aOvfValues[a], + bstrFinalValue.raw(), + strExtraConfigValue.c_str(), + i, a); + } + else if (fDiskChanged && !fControllerChanged && fControllerPortChanged) + { + RTPrintf(Appliance::tr("%2u: Hard disk image specified with --disk and --port: source image=%ls, target path=%ls, %s\n" + " (change controller with \"--vsys %u --unit %u --controller \")\n"), + a, + aOvfValues[a], + bstrFinalValue.raw(), + strExtraConfigValue.c_str(), + i, a); + } + else if (!fDiskChanged && fControllerChanged && fControllerPortChanged) + { + RTPrintf(Appliance::tr("%2u: Hard disk image specified with --controller and --port: source image=%ls, target path=%ls, %s\n" + " (change target path with \"--vsys %u --unit %u --disk path\")\n"), + a, + aOvfValues[a], + bstrFinalValue.raw(), + strExtraConfigValue.c_str(), + i, a); + } + else if (!fDiskChanged && !fControllerChanged && fControllerPortChanged) + { + RTPrintf(Appliance::tr("%2u: Hard disk image specified with --port: source image=%ls, target path=%ls, %s\n" + " (change target path with \"--vsys %u --unit %u --disk path\";\n" + " change controller with \"--vsys %u --unit %u --controller \")\n"), + a, + aOvfValues[a], + bstrFinalValue.raw(), + strExtraConfigValue.c_str(), + i, a, + i, a); + } + else if (!fDiskChanged && fControllerChanged && !fControllerPortChanged) + { + RTPrintf(Appliance::tr("%2u: Hard disk image specified with --controller: source image=%ls, target path=%ls, %s\n" + " (change target path with \"--vsys %u --unit %u --disk path\";\n" + " change controller port with \"--vsys %u --unit %u --port \")\n"), + a, + aOvfValues[a], + bstrFinalValue.raw(), + strExtraConfigValue.c_str(), + i, a, + i, a); + } + else if (fDiskChanged && fControllerChanged && fControllerPortChanged) + { + RTPrintf(Appliance::tr("%2u: Hard disk image specified with --disk and --controller and --port: source image=%ls, target path=%ls, %s\n"), + a, + aOvfValues[a], + bstrFinalValue.raw(), + strExtraConfigValue.c_str()); + } + 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 pVirtualBox = arg->virtualBox; + ComPtr systemProperties; + com::SafeIfaceArray 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 extensions; + + for (unsigned j = 0; j < mediumFormats.size(); ++j) + { + com::SafeArray deviceType; + ComPtr 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(Appliance::tr("%2u: Hard disk image: source image=%ls, target path=%ls, %s\n" + " (change target path with \"--vsys %u --unit %u --disk path\";\n" + " change controller with \"--vsys %u --unit %u --controller \";\n" + " change controller port with \"--vsys %u --unit %u --port \";\n" + " disable with \"--vsys %u --unit %u --ignore\")\n"), + a, aOvfValues[a], bstrFinalValue.raw(), strExtraConfigValue.c_str(), + i, a, + i, a, + i, a, + i, a); + } + } + break; + + case VirtualSystemDescriptionType_CDROM: + if (fIgnoreThis) + { + RTPrintf(Appliance::tr("%2u: CD-ROM -- disabled\n"), + a); + aEnabled[a] = false; + } + else + RTPrintf(Appliance::tr("%2u: CD-ROM\n" + " (disable with \"--vsys %u --unit %u --ignore\")\n"), + a, i, a); + break; + + case VirtualSystemDescriptionType_Floppy: + if (fIgnoreThis) + { + RTPrintf(Appliance::tr("%2u: Floppy -- disabled\n"), + a); + aEnabled[a] = false; + } + else + RTPrintf(Appliance::tr("%2u: Floppy\n" + " (disable with \"--vsys %u --unit %u --ignore\")\n"), + a, i, a); + break; + + case VirtualSystemDescriptionType_NetworkAdapter: + RTPrintf(Appliance::tr("%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(Appliance::tr("%2u: USB controller -- disabled\n"), + a); + aEnabled[a] = false; + } + else + RTPrintf(Appliance::tr("%2u: USB controller\n" + " (disable with \"--vsys %u --unit %u --ignore\")\n"), + a, i, a); + break; + + case VirtualSystemDescriptionType_SoundCard: + if (fIgnoreThis) + { + RTPrintf(Appliance::tr("%2u: Sound card \"%ls\" -- disabled\n"), + a, + aOvfValues[a]); + aEnabled[a] = false; + } + else + RTPrintf(Appliance::tr("%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(Appliance::tr("%2u: VM settings file name specified with --settingsfile: \"%ls\"\n"), + a, bstrFinalValue.raw()); + } + else + RTPrintf(Appliance::tr("%2u: Suggested VM settings file name \"%ls\"\n" + " (change with \"--vsys %u --settingsfile \")\n"), + a, bstrFinalValue.raw(), i); + break; + + case VirtualSystemDescriptionType_BaseFolder: + if (findArgValue(strOverride, pmapArgs, "basefolder")) + { + bstrFinalValue = strOverride; + RTPrintf(Appliance::tr("%2u: VM base folder specified with --basefolder: \"%ls\"\n"), + a, bstrFinalValue.raw()); + } + else + RTPrintf(Appliance::tr("%2u: Suggested VM base folder \"%ls\"\n" + " (change with \"--vsys %u --basefolder \")\n"), + a, bstrFinalValue.raw(), i); + break; + + case VirtualSystemDescriptionType_PrimaryGroup: + if (findArgValue(strOverride, pmapArgs, "group")) + { + bstrFinalValue = strOverride; + RTPrintf(Appliance::tr("%2u: VM group specified with --group: \"%ls\"\n"), + a, bstrFinalValue.raw()); + } + else + RTPrintf(Appliance::tr("%2u: Suggested VM group \"%ls\"\n" + " (change with \"--vsys %u --group \")\n"), + a, bstrFinalValue.raw(), i); + break; + + case VirtualSystemDescriptionType_CloudInstanceShape: + RTPrintf(Appliance::tr("%2u: Suggested cloud shape \"%ls\"\n"), + a, bstrFinalValue.raw()); + break; + + case VirtualSystemDescriptionType_CloudBucket: + if (findArgValue(strOverride, pmapArgs, "cloudbucket")) + { + bstrFinalValue = strOverride; + RTPrintf(Appliance::tr("%2u: Cloud bucket id specified with --cloudbucket: \"%ls\"\n"), + a, bstrFinalValue.raw()); + } + else + RTPrintf(Appliance::tr("%2u: Suggested cloud bucket id \"%ls\"\n" + " (change with \"--cloud %u --cloudbucket \")\n"), + a, bstrFinalValue.raw(), i); + break; + + case VirtualSystemDescriptionType_CloudProfileName: + if (findArgValue(strOverride, pmapArgs, "cloudprofile")) + { + bstrFinalValue = strOverride; + RTPrintf(Appliance::tr("%2u: Cloud profile name specified with --cloudprofile: \"%ls\"\n"), + a, bstrFinalValue.raw()); + } + else + RTPrintf(Appliance::tr("%2u: Suggested cloud profile name \"%ls\"\n" + " (change with \"--cloud %u --cloudprofile \")\n"), + a, bstrFinalValue.raw(), i); + break; + + case VirtualSystemDescriptionType_CloudInstanceId: + if (findArgValue(strOverride, pmapArgs, "cloudinstanceid")) + { + bstrFinalValue = strOverride; + RTPrintf(Appliance::tr("%2u: Cloud instance id specified with --cloudinstanceid: \"%ls\"\n"), + a, bstrFinalValue.raw()); + } + else + RTPrintf(Appliance::tr("%2u: Suggested cloud instance id \"%ls\"\n" + " (change with \"--cloud %u --cloudinstanceid \")\n"), + a, bstrFinalValue.raw(), i); + break; + + case VirtualSystemDescriptionType_CloudImageId: + RTPrintf(Appliance::tr("%2u: Suggested cloud base image id \"%ls\"\n"), + a, bstrFinalValue.raw()); + break; + case VirtualSystemDescriptionType_CloudDomain: + case VirtualSystemDescriptionType_CloudBootDiskSize: + case VirtualSystemDescriptionType_CloudOCIVCN: + case VirtualSystemDescriptionType_CloudPublicIP: + case VirtualSystemDescriptionType_CloudOCISubnet: + case VirtualSystemDescriptionType_CloudKeepObject: + case VirtualSystemDescriptionType_CloudLaunchInstance: + case VirtualSystemDescriptionType_CloudInstanceState: + case VirtualSystemDescriptionType_CloudImageState: + case VirtualSystemDescriptionType_Miscellaneous: + case VirtualSystemDescriptionType_CloudInstanceDisplayName: + case VirtualSystemDescriptionType_CloudImageDisplayName: + case VirtualSystemDescriptionType_CloudOCILaunchMode: + case VirtualSystemDescriptionType_CloudPrivateIP: + case VirtualSystemDescriptionType_CloudBootVolumeId: + case VirtualSystemDescriptionType_CloudOCIVCNCompartment: + case VirtualSystemDescriptionType_CloudOCISubnetCompartment: + case VirtualSystemDescriptionType_CloudPublicSSHKey: + case VirtualSystemDescriptionType_BootingFirmware: + case VirtualSystemDescriptionType_CloudInitScriptPath: + case VirtualSystemDescriptionType_CloudCompartmentId: + case VirtualSystemDescriptionType_CloudShapeCpus: + case VirtualSystemDescriptionType_CloudShapeMemory: + case VirtualSystemDescriptionType_CloudInstanceMetadata: + case VirtualSystemDescriptionType_CloudInstanceFreeFormTags: + case VirtualSystemDescriptionType_CloudImageFreeFormTags: + /** @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(Appliance::tr("Cannot import until the license agreement listed above is accepted.")); + else if (cLicensesInTheWay > 1) + RTMsgError(Appliance::tr("Cannot import until the %c license agreements listed above are accepted."), + cLicensesInTheWay); + + if (!cLicensesInTheWay && fExecute) + { + // go! + ComPtr progress; + CHECK_ERROR_BREAK(pAppliance, + ImportMachines(ComSafeArrayAsInParam(options), progress.asOutParam())); + + hrc = showProgress(progress); + CHECK_PROGRESS_ERROR_RET(progress, (Appliance::tr("Appliance import failed")), RTEXITCODE_FAILURE); + + if (SUCCEEDED(hrc)) + RTPrintf(Appliance::tr("Successfully imported the appliance.\n")); + } + } // end if (aVirtualSystemDescriptions.size() > 0) + } while (0); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +static int parseExportOptions(const char *psz, com::SafeArray *options) +{ + int vrc = VINF_SUCCESS; + while (psz && *psz && RT_SUCCESS(vrc)) + { + 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 + vrc = VERR_PARSE_ERROR; + } + if (pszComma) + psz += len + 1; + else + psz += len; + } + + return vrc; +} + +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 + { "--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 }, + { "--cloudlaunchmode", 'M', RTGETOPT_REQ_STRING }, + { "--cloudprivateip", 'i', RTGETOPT_REQ_STRING }, + { "--cloudinitscriptpath", 'I', RTGETOPT_REQ_STRING }, +}; + +RTEXITCODE handleExportAppliance(HandlerArg *a) +{ + HRESULT hrc = S_OK; + + Utf8Str strOutputFile; + Utf8Str strOvfFormat("ovf-1.0"); // the default export version + bool fManifest = false; // the default + APPLIANCETYPE enmApplType = NOT_SET; + bool fExportISOImages = false; // the default + com::SafeArray options; + std::list< ComPtr > 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(Appliance::tr("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 (enmApplType == NOT_SET) + enmApplType = LOCAL; + + if (enmApplType != LOCAL) + return errorSyntax(Appliance::tr("Option \"%s\" can't be used together with \"--cloud\" option."), + GetState.pDef->pszLong); + if (ValueUnion.u32 == (uint32_t)-1) + return errorSyntax(Appliance::tr("Value of option \"%s\" is out of range."), + GetState.pDef->pszLong); + + ulCurVsys = ValueUnion.u32; + break; + + case 'V': // --vmname + if (enmApplType == NOT_SET) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys or --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["vmname"] = ValueUnion.psz; + break; + + case 'p': // --product + if (enmApplType != LOCAL) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["product"] = ValueUnion.psz; + break; + + case 'P': // --producturl + if (enmApplType != LOCAL) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["producturl"] = ValueUnion.psz; + break; + + case 'n': // --vendor + if (enmApplType != LOCAL) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["vendor"] = ValueUnion.psz; + break; + + case 'N': // --vendorurl + if (enmApplType != LOCAL) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["vendorurl"] = ValueUnion.psz; + break; + + case 'v': // --version + if (enmApplType != LOCAL) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["version"] = ValueUnion.psz; + break; + + case 'd': // --description + if (enmApplType != LOCAL) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["description"] = ValueUnion.psz; + break; + + case 'e': // --eula + if (enmApplType != LOCAL) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["eula"] = ValueUnion.psz; + break; + + case 'E': // --eulafile + if (enmApplType != LOCAL) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["eulafile"] = ValueUnion.psz; + break; + + case 'O': // --options + if (RT_FAILURE(parseExportOptions(ValueUnion.psz, &options))) + return errorArgument(Appliance::tr("Invalid export options '%s'\n"), ValueUnion.psz); + break; + + /*--cloud and --vsys are orthogonal, only one must be presented*/ + case 'C': // --cloud + if (enmApplType == NOT_SET) + enmApplType = CLOUD; + + if (enmApplType != CLOUD) + return errorSyntax(Appliance::tr("Option \"%s\" can't be used together with \"--vsys\" option."), + GetState.pDef->pszLong); + if (ValueUnion.u32 == (uint32_t)-1) + return errorSyntax(Appliance::tr("Value of option \"%s\" is out of range."), + GetState.pDef->pszLong); + + ulCurVsys = ValueUnion.u32; + break; + + /* Cloud export settings */ + case 'S': // --cloudshape + if (enmApplType != CLOUD) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["cloudshape"] = ValueUnion.psz; + break; + + case 'D': // --clouddomain + if (enmApplType != CLOUD) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["clouddomain"] = ValueUnion.psz; + break; + + case 'R': // --clouddisksize + if (enmApplType != CLOUD) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["clouddisksize"] = ValueUnion.psz; + break; + + case 'B': // --cloudbucket + if (enmApplType != CLOUD) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["cloudbucket"] = ValueUnion.psz; + break; + + case 'Q': // --cloudocivcn + if (enmApplType != CLOUD) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["cloudocivcn"] = ValueUnion.psz; + break; + + case 'A': // --cloudpublicip + if (enmApplType != CLOUD) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["cloudpublicip"] = ValueUnion.psz; + break; + + case 'i': /* --cloudprivateip */ + if (enmApplType != CLOUD) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["cloudprivateip"] = ValueUnion.psz; + break; + + case 'F': // --cloudprofile + if (enmApplType != CLOUD) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["cloudprofile"] = ValueUnion.psz; + break; + + case 'T': // --cloudocisubnet + if (enmApplType != CLOUD) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["cloudocisubnet"] = ValueUnion.psz; + break; + + case 'K': // --cloudkeepobject + if (enmApplType != CLOUD) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["cloudkeepobject"] = ValueUnion.psz; + break; + + case 'L': // --cloudlaunchinstance + if (enmApplType != CLOUD) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["cloudlaunchinstance"] = ValueUnion.psz; + break; + + case 'M': /* --cloudlaunchmode */ + if (enmApplType != CLOUD) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["cloudlaunchmode"] = ValueUnion.psz; + break; + + case 'I': // --cloudinitscriptpath + if (enmApplType != CLOUD) + return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."), + GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["cloudinitscriptpath"] = ValueUnion.psz; + break; + + case VINF_GETOPT_NOT_OPTION: + { + Utf8Str strMachine(ValueUnion.psz); + // must be machine: try UUID or name + ComPtr 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(Appliance::tr("unhandled option: -%c"), c); + else + return errorSyntax(Appliance::tr("unhandled option: %i"), c); + } + else if (c == VERR_GETOPT_UNKNOWN_OPTION) + return errorSyntax(Appliance::tr("unknown option: %s"), ValueUnion.psz); + else if (ValueUnion.pDef) + return errorSyntax("%s: %Rrs", ValueUnion.pDef->pszLong, c); + else + return errorSyntax("%Rrs", c); + } + + if (FAILED(hrc)) + break; + } + + if (FAILED(hrc)) + break; + + if (llMachines.empty()) + return errorSyntax(Appliance::tr("At least one machine must be specified with the export command.")); + + /* Last check after parsing all arguments */ + if (strOutputFile.isEmpty()) + return errorSyntax(Appliance::tr("Missing --output argument with export command.")); + + if (enmApplType == NOT_SET) + enmApplType = LOCAL; + + // 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(Appliance::tr("Invalid index %RI32 with -vsys option; you specified only %zu virtual system(s).", + "", llMachines.size()), + ulVsys, llMachines.size()); + } + + ComPtr 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()); + + /* + * The first stage - export machine/s to the Cloud or into the + * OVA/OVF format on the local host. + */ + + /* VSDList is needed for the second stage where we launch the cloud instances if it was requested by user */ + std::list< ComPtr > VSDList; + std::list< ComPtr >::iterator itM; + uint32_t i=0; + for (itM = llMachines.begin(); + itM != llMachines.end(); + ++itM, ++i) + { + ComPtr pMachine = *itM; + ComPtr 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(), NULL); + } + else if (itD->first == "product") + pVSD->AddDescription(VirtualSystemDescriptionType_Product, + Bstr(itD->second).raw(), NULL); + else if (itD->first == "producturl") + pVSD->AddDescription(VirtualSystemDescriptionType_ProductUrl, + Bstr(itD->second).raw(), NULL); + else if (itD->first == "vendor") + pVSD->AddDescription(VirtualSystemDescriptionType_Vendor, + Bstr(itD->second).raw(), NULL); + else if (itD->first == "vendorurl") + pVSD->AddDescription(VirtualSystemDescriptionType_VendorUrl, + Bstr(itD->second).raw(), NULL); + else if (itD->first == "version") + pVSD->AddDescription(VirtualSystemDescriptionType_Version, + Bstr(itD->second).raw(), NULL); + else if (itD->first == "description") + pVSD->AddDescription(VirtualSystemDescriptionType_Description, + Bstr(itD->second).raw(), NULL); + else if (itD->first == "eula") + pVSD->AddDescription(VirtualSystemDescriptionType_License, + Bstr(itD->second).raw(), NULL); + 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(), NULL); + RTFileReadAllFree(pvFile, cbFile); + } + else + { + RTMsgError(Appliance::tr("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(), NULL); + else if (itD->first == "clouddomain") + pVSD->AddDescription(VirtualSystemDescriptionType_CloudDomain, + Bstr(itD->second).raw(), NULL); + else if (itD->first == "clouddisksize") + pVSD->AddDescription(VirtualSystemDescriptionType_CloudBootDiskSize, + Bstr(itD->second).raw(), NULL); + else if (itD->first == "cloudbucket") + pVSD->AddDescription(VirtualSystemDescriptionType_CloudBucket, + Bstr(itD->second).raw(), NULL); + else if (itD->first == "cloudocivcn") + pVSD->AddDescription(VirtualSystemDescriptionType_CloudOCIVCN, + Bstr(itD->second).raw(), NULL); + else if (itD->first == "cloudpublicip") + pVSD->AddDescription(VirtualSystemDescriptionType_CloudPublicIP, + Bstr(itD->second).raw(), NULL); + else if (itD->first == "cloudprivateip") + pVSD->AddDescription(VirtualSystemDescriptionType_CloudPrivateIP, + Bstr(itD->second).raw(), NULL); + else if (itD->first == "cloudprofile") + pVSD->AddDescription(VirtualSystemDescriptionType_CloudProfileName, + Bstr(itD->second).raw(), NULL); + else if (itD->first == "cloudocisubnet") + pVSD->AddDescription(VirtualSystemDescriptionType_CloudOCISubnet, + Bstr(itD->second).raw(), NULL); + else if (itD->first == "cloudkeepobject") + pVSD->AddDescription(VirtualSystemDescriptionType_CloudKeepObject, + Bstr(itD->second).raw(), NULL); + else if (itD->first == "cloudlaunchmode") + pVSD->AddDescription(VirtualSystemDescriptionType_CloudOCILaunchMode, + Bstr(itD->second).raw(), NULL); + else if (itD->first == "cloudlaunchinstance") + pVSD->AddDescription(VirtualSystemDescriptionType_CloudLaunchInstance, + Bstr(itD->second).raw(), NULL); + else if (itD->first == "cloudinitscriptpath") + pVSD->AddDescription(VirtualSystemDescriptionType_CloudInitScriptPath, + Bstr(itD->second).raw(), NULL); + + } + } + + VSDList.push_back(pVSD);//store vsd for the possible second stage + } + + if (FAILED(hrc)) + break; + + /* Query required passwords and supply them to the appliance. */ + com::SafeArray aIdentifiers; + + CHECK_ERROR_BREAK(pAppliance, GetPasswordIds(ComSafeArrayAsOutParam(aIdentifiers))); + + if (aIdentifiers.size() > 0) + { + com::SafeArray aPasswords(aIdentifiers.size()); + RTPrintf(Appliance::tr("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, Appliance::tr("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 progress; + CHECK_ERROR_BREAK(pAppliance, Write(Bstr(strOvfFormat).raw(), + ComSafeArrayAsInParam(options), + Bstr(pszAbsFilePath).raw(), + progress.asOutParam())); + RTStrFree(pszAbsFilePath); + + hrc = showProgress(progress); + CHECK_PROGRESS_ERROR_RET(progress, (Appliance::tr("Appliance write failed")), RTEXITCODE_FAILURE); + + if (SUCCEEDED(hrc)) + RTPrintf(Appliance::tr("Successfully exported %d machine(s).\n", "", llMachines.size()), llMachines.size()); + + /* + * The second stage for the cloud case + */ + if (enmApplType == CLOUD) + { + /* Launch the exported VM if the appropriate flag had been set on the first stage */ + for (std::list< ComPtr >::iterator itVSD = VSDList.begin(); + itVSD != VSDList.end(); + ++itVSD) + { + ComPtr pVSD = *itVSD; + + com::SafeArray retTypes; + com::SafeArray aRefs; + com::SafeArray aOvfValues; + com::SafeArray aVBoxValues; + com::SafeArray aExtraConfigValues; + + CHECK_ERROR_BREAK(pVSD, GetDescriptionByType(VirtualSystemDescriptionType_CloudLaunchInstance, + ComSafeArrayAsOutParam(retTypes), + ComSafeArrayAsOutParam(aRefs), + ComSafeArrayAsOutParam(aOvfValues), + ComSafeArrayAsOutParam(aVBoxValues), + ComSafeArrayAsOutParam(aExtraConfigValues))); + + Utf8Str flagCloudLaunchInstance(Bstr(aVBoxValues[0]).raw()); + retTypes.setNull(); aRefs.setNull(); aOvfValues.setNull(); aVBoxValues.setNull(); aExtraConfigValues.setNull(); + + if (flagCloudLaunchInstance.equals("true")) + { + /* Getting the short provider name */ + Bstr bstrCloudProviderShortName(strOutputFile.c_str(), strOutputFile.find("://")); + + ComPtr pVirtualBox = a->virtualBox; + ComPtr pCloudProviderManager; + CHECK_ERROR_BREAK(pVirtualBox, COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam())); + + ComPtr pCloudProvider; + CHECK_ERROR_BREAK(pCloudProviderManager, + GetProviderByShortName(bstrCloudProviderShortName.raw(), pCloudProvider.asOutParam())); + + CHECK_ERROR_BREAK(pVSD, GetDescriptionByType(VirtualSystemDescriptionType_CloudProfileName, + ComSafeArrayAsOutParam(retTypes), + ComSafeArrayAsOutParam(aRefs), + ComSafeArrayAsOutParam(aOvfValues), + ComSafeArrayAsOutParam(aVBoxValues), + ComSafeArrayAsOutParam(aExtraConfigValues))); + + ComPtr pCloudProfile; + CHECK_ERROR_BREAK(pCloudProvider, GetProfileByName(Bstr(aVBoxValues[0]).raw(), pCloudProfile.asOutParam())); + retTypes.setNull(); aRefs.setNull(); aOvfValues.setNull(); aVBoxValues.setNull(); aExtraConfigValues.setNull(); + + ComObjPtr oCloudClient; + CHECK_ERROR_BREAK(pCloudProfile, CreateCloudClient(oCloudClient.asOutParam())); + RTPrintf(Appliance::tr("Creating a cloud instance...\n")); + + ComPtr progress1; + CHECK_ERROR_BREAK(oCloudClient, LaunchVM(pVSD, progress1.asOutParam())); + hrc = showProgress(progress1); + CHECK_PROGRESS_ERROR_RET(progress1, (Appliance::tr("Creating the cloud instance failed")), + RTEXITCODE_FAILURE); + + if (SUCCEEDED(hrc)) + { + CHECK_ERROR_BREAK(pVSD, GetDescriptionByType(VirtualSystemDescriptionType_CloudInstanceId, + ComSafeArrayAsOutParam(retTypes), + ComSafeArrayAsOutParam(aRefs), + ComSafeArrayAsOutParam(aOvfValues), + ComSafeArrayAsOutParam(aVBoxValues), + ComSafeArrayAsOutParam(aExtraConfigValues))); + + RTPrintf(Appliance::tr("A cloud instance with id '%s' (provider '%s') was created\n"), + Utf8Str(Bstr(aVBoxValues[0]).raw()).c_str(), + Utf8Str(bstrCloudProviderShortName.raw()).c_str()); + retTypes.setNull(); aRefs.setNull(); aOvfValues.setNull(); aVBoxValues.setNull(); aExtraConfigValues.setNull(); + } + } + } + } + } while (0); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + + +/********************************************************************************************************************************* +* signova * +*********************************************************************************************************************************/ + +/** + * Reads the OVA and saves the manifest and signed status. + * + * @returns VBox status code (fully messaged). + * @param pszOva The name of the OVA. + * @param iVerbosity The noise level. + * @param fReSign Whether it is acceptable to have an existing signature + * in the OVA or not. + * @param phVfsFssOva Where to return the OVA file system stream handle. + * This has been opened for updating and we're positioned + * at the end of the stream. + * @param pStrManifestName Where to return the manifest name. + * @param phVfsManifest Where to return the manifest file handle (copy in mem). + * @param phVfsOldSignature Where to return the handle to the old signature object. + * + * @note Caller must clean up return values on failure too! + */ +static int openOvaAndGetManifestAndOldSignature(const char *pszOva, unsigned iVerbosity, bool fReSign, + PRTVFSFSSTREAM phVfsFssOva, Utf8Str *pStrManifestName, + PRTVFSFILE phVfsManifest, PRTVFSOBJ phVfsOldSignature) +{ + /* + * Clear return values. + */ + *phVfsFssOva = NIL_RTVFSFSSTREAM; + pStrManifestName->setNull(); + *phVfsManifest = NIL_RTVFSFILE; + *phVfsOldSignature = NIL_RTVFSOBJ; + + /* + * Open the file as a tar file system stream. + */ + RTVFSFILE hVfsFileOva; + int vrc = RTVfsFileOpenNormal(pszOva, RTFILE_O_OPEN | RTFILE_O_READWRITE | RTFILE_O_DENY_WRITE, &hVfsFileOva); + if (RT_FAILURE(vrc)) + return RTMsgErrorExitFailure(Appliance::tr("Failed to open OVA '%s' for updating: %Rrc"), pszOva, vrc); + + RTVFSFSSTREAM hVfsFssOva; + vrc = RTZipTarFsStreamForFile(hVfsFileOva, RTZIPTARFORMAT_DEFAULT, RTZIPTAR_C_UPDATE, &hVfsFssOva); + RTVfsFileRelease(hVfsFileOva); + if (RT_FAILURE(vrc)) + return RTMsgErrorExitFailure(Appliance::tr("Failed to open OVA '%s' as a TAR file: %Rrc"), pszOva, vrc); + *phVfsFssOva = hVfsFssOva; + + /* + * Scan the objects in the stream and locate the manifest and any existing cert file. + */ + if (iVerbosity >= 2) + RTMsgInfo(Appliance::tr("Scanning OVA '%s' for a manifest and signature..."), pszOva); + char *pszSignatureName = NULL; + for (;;) + { + /* + * Retrive the next object. + */ + char *pszName; + RTVFSOBJTYPE enmType; + RTVFSOBJ hVfsObj; + vrc = RTVfsFsStrmNext(hVfsFssOva, &pszName, &enmType, &hVfsObj); + if (RT_FAILURE(vrc)) + { + if (vrc == VERR_EOF) + vrc = VINF_SUCCESS; + else + RTMsgError(Appliance::tr("RTVfsFsStrmNext returned %Rrc"), vrc); + break; + } + + if (iVerbosity > 2) + RTMsgInfo(" %s %s\n", RTVfsTypeName(enmType), pszName); + + /* + * Should we process this entry? + */ + const char *pszSuffix = RTPathSuffix(pszName); + if ( pszSuffix + && RTStrICmpAscii(pszSuffix, ".mf") == 0 + && (enmType == RTVFSOBJTYPE_IO_STREAM || enmType == RTVFSOBJTYPE_FILE)) + { + if (*phVfsManifest != NIL_RTVFSFILE) + vrc = RTMsgErrorRc(VERR_DUPLICATE, Appliance::tr("OVA contains multiple manifests! first: %s second: %s"), + pStrManifestName->c_str(), pszName); + else if (pszSignatureName) + vrc = RTMsgErrorRc(VERR_WRONG_ORDER, + Appliance::tr("Unsupported OVA file ordering! Signature file ('%s') as succeeded by '%s'."), + pszSignatureName, pszName); + else + { + if (iVerbosity >= 2) + RTMsgInfo(Appliance::tr("Found manifest file: %s"), pszName); + vrc = pStrManifestName->assignNoThrow(pszName); + if (RT_SUCCESS(vrc)) + { + RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj); + Assert(hVfsIos != NIL_RTVFSIOSTREAM); + vrc = RTVfsMemorizeIoStreamAsFile(hVfsIos, RTFILE_O_READ, phVfsManifest); + RTVfsIoStrmRelease(hVfsIos); /* consumes stream handle. */ + if (RT_FAILURE(vrc)) + vrc = RTMsgErrorRc(VERR_DUPLICATE, Appliance::tr("Failed to memorize the manifest: %Rrc"), vrc); + } + else + RTMsgError(Appliance::tr("Out of memory!")); + } + } + else if ( pszSuffix + && RTStrICmpAscii(pszSuffix, ".cert") == 0 + && (enmType == RTVFSOBJTYPE_IO_STREAM || enmType == RTVFSOBJTYPE_FILE)) + { + if (*phVfsOldSignature != NIL_RTVFSOBJ) + vrc = RTMsgErrorRc(VERR_WRONG_ORDER, Appliance::tr("Multiple signature files! (%s)"), pszName); + else + { + if (iVerbosity >= 2) + RTMsgInfo(Appliance::tr("Found existing signature file: %s"), pszName); + pszSignatureName = pszName; + *phVfsOldSignature = hVfsObj; + pszName = NULL; + hVfsObj = NIL_RTVFSOBJ; + } + } + else if (pszSignatureName) + vrc = RTMsgErrorRc(VERR_WRONG_ORDER, + Appliance::tr("Unsupported OVA file ordering! Signature file ('%s') as succeeded by '%s'."), + pszSignatureName, pszName); + + /* + * Release the current object and string. + */ + RTVfsObjRelease(hVfsObj); + RTStrFree(pszName); + if (RT_FAILURE(vrc)) + break; + } + + /* + * Complain if no manifest. + */ + if (RT_SUCCESS(vrc) && *phVfsManifest == NIL_RTVFSFILE) + vrc = RTMsgErrorRc(VERR_NOT_FOUND, Appliance::tr("The OVA contains no manifest and cannot be signed!")); + else if (RT_SUCCESS(vrc) && *phVfsOldSignature != NIL_RTVFSOBJ && !fReSign) + vrc = RTMsgErrorRc(VERR_ALREADY_EXISTS, + Appliance::tr("The OVA is already signed ('%s')! (Use the --force option to force re-signing it.)"), + pszSignatureName); + + RTStrFree(pszSignatureName); + return vrc; +} + + +/** + * Continues where openOvaAndGetManifestAndOldSignature() left off and writes + * the signature file to the OVA. + * + * When @a hVfsOldSignature isn't NIL, the old signature it represent will be + * replaced. The open function has already made sure there isn't anything + * following the .cert file in that case. + */ +static int updateTheOvaSignature(RTVFSFSSTREAM hVfsFssOva, const char *pszOva, const char *pszSignatureName, + RTVFSFILE hVfsFileSignature, RTVFSOBJ hVfsOldSignature, unsigned iVerbosity) +{ + if (iVerbosity > 1) + RTMsgInfo(Appliance::tr("Writing '%s' to the OVA..."), pszSignatureName); + + /* + * Truncate the file at the old signature, if present. + */ + int vrc; + if (hVfsOldSignature != NIL_RTVFSOBJ) + { + vrc = RTZipTarFsStreamTruncate(hVfsFssOva, hVfsOldSignature, false /*fAfter*/); + if (RT_FAILURE(vrc)) + return RTMsgErrorRc(vrc, Appliance::tr("RTZipTarFsStreamTruncate failed on '%s': %Rrc"), pszOva, vrc); + } + + /* + * Append the signature file. We have to rewind it first or + * we'll end up with VERR_EOF, probably not a great idea... + */ + vrc = RTVfsFileSeek(hVfsFileSignature, 0, RTFILE_SEEK_BEGIN, NULL); + if (RT_FAILURE(vrc)) + return RTMsgErrorRc(vrc, Appliance::tr("RTVfsFileSeek(hVfsFileSignature) failed: %Rrc"), vrc); + + RTVFSOBJ hVfsObj = RTVfsObjFromFile(hVfsFileSignature); + vrc = RTVfsFsStrmAdd(hVfsFssOva, pszSignatureName, hVfsObj, 0 /*fFlags*/); + RTVfsObjRelease(hVfsObj); + if (RT_FAILURE(vrc)) + return RTMsgErrorRc(vrc, Appliance::tr("RTVfsFsStrmAdd('%s') failed on '%s': %Rrc"), pszSignatureName, pszOva, vrc); + + /* + * Terminate the file system stream. + */ + vrc = RTVfsFsStrmEnd(hVfsFssOva); + if (RT_FAILURE(vrc)) + return RTMsgErrorRc(vrc, Appliance::tr("RTVfsFsStrmEnd failed on '%s': %Rrc"), pszOva, vrc); + + return VINF_SUCCESS; +} + + +/** + * Worker for doCheckPkcs7Signature. + */ +static int doCheckPkcs7SignatureWorker(PRTCRPKCS7CONTENTINFO pContentInfo, void const *pvManifest, size_t cbManifest, + unsigned iVerbosity, const char *pszTag, PRTERRINFOSTATIC pErrInfo) +{ + int vrc; + + /* + * It must be signedData. + */ + if (RTCrPkcs7ContentInfo_IsSignedData(pContentInfo)) + { + PRTCRPKCS7SIGNEDDATA pSignedData = pContentInfo->u.pSignedData; + + /* + * Inside the signedData there must be just 'data'. + */ + if (!strcmp(pSignedData->ContentInfo.ContentType.szObjId, RTCR_PKCS7_DATA_OID)) + { + /* + * Check that things add up. + */ + vrc = RTCrPkcs7SignedData_CheckSanity(pSignedData, + RTCRPKCS7SIGNEDDATA_SANITY_F_ONLY_KNOWN_HASH + | RTCRPKCS7SIGNEDDATA_SANITY_F_SIGNING_CERT_PRESENT, + RTErrInfoInitStatic(pErrInfo), "SD"); + if (RT_SUCCESS(vrc)) + { + if (iVerbosity > 2 && pszTag == NULL) + RTMsgInfo(Appliance::tr(" Successfully decoded the PKCS#7/CMS signature...")); + + /* + * Check that we can verify the signed data, but skip certificate validate as + * we probably don't necessarily have the correct root certs handy here. + */ + RTTIMESPEC Now; + vrc = RTCrPkcs7VerifySignedDataWithExternalData(pContentInfo, RTCRPKCS7VERIFY_SD_F_TRUST_ALL_CERTS, + NIL_RTCRSTORE /*hAdditionalCerts*/, + NIL_RTCRSTORE /*hTrustedCerts*/, + RTTimeNow(&Now), + NULL /*pfnVerifyCert*/, NULL /*pvUser*/, + pvManifest, cbManifest, RTErrInfoInitStatic(pErrInfo)); + if (RT_SUCCESS(vrc)) + { + if (iVerbosity > 1 && pszTag != NULL) + RTMsgInfo(Appliance::tr(" Successfully verified the PKCS#7/CMS signature")); + } + else + vrc = RTMsgErrorRc(vrc, Appliance::tr("Failed to verify the PKCS#7/CMS signature: %Rrc%RTeim"), + vrc, &pErrInfo->Core); + } + else + RTMsgError(Appliance::tr("RTCrPkcs7SignedData_CheckSanity failed on PKCS#7/CMS signature: %Rrc%RTeim"), + vrc, &pErrInfo->Core); + + } + else + vrc = RTMsgErrorRc(VERR_WRONG_TYPE, Appliance::tr("PKCS#7/CMS signature inner ContentType isn't 'data' but: %s"), + pSignedData->ContentInfo.ContentType.szObjId); + } + else + vrc = RTMsgErrorRc(VERR_WRONG_TYPE, Appliance::tr("PKCS#7/CMD signature is not 'signedData': %s"), + pContentInfo->ContentType.szObjId); + return vrc; +} + +/** + * For testing the decoding side. + */ +static int doCheckPkcs7Signature(void const *pvSignature, size_t cbSignature, PCRTCRX509CERTIFICATE pCertificate, + RTCRSTORE hIntermediateCerts, void const *pvManifest, size_t cbManifest, + unsigned iVerbosity, PRTERRINFOSTATIC pErrInfo) +{ + RT_NOREF(pCertificate, hIntermediateCerts); + + RTASN1CURSORPRIMARY PrimaryCursor; + RTAsn1CursorInitPrimary(&PrimaryCursor, pvSignature, (uint32_t)cbSignature, RTErrInfoInitStatic(pErrInfo), + &g_RTAsn1DefaultAllocator, 0, "Signature"); + + RTCRPKCS7CONTENTINFO ContentInfo; + RT_ZERO(ContentInfo); + int vrc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, &ContentInfo, "CI"); + if (RT_SUCCESS(vrc)) + { + if (iVerbosity > 5) + RTAsn1Dump(&ContentInfo.SeqCore.Asn1Core, 0 /*fFlags*/, 0 /*uLevel*/, RTStrmDumpPrintfV, g_pStdOut); + + vrc = doCheckPkcs7SignatureWorker(&ContentInfo, pvManifest, cbManifest, iVerbosity, NULL, pErrInfo); + if (RT_SUCCESS(vrc)) + { + /* + * Clone it and repeat. This is to catch IPRT paths assuming + * that encoded data is always on hand. + */ + RTCRPKCS7CONTENTINFO ContentInfo2; + vrc = RTCrPkcs7ContentInfo_Clone(&ContentInfo2, &ContentInfo, &g_RTAsn1DefaultAllocator); + if (RT_SUCCESS(vrc)) + { + vrc = doCheckPkcs7SignatureWorker(&ContentInfo2, pvManifest, cbManifest, iVerbosity, "cloned", pErrInfo); + RTCrPkcs7ContentInfo_Delete(&ContentInfo2); + } + else + vrc = RTMsgErrorRc(vrc, Appliance::tr("RTCrPkcs7ContentInfo_Clone failed: %Rrc"), vrc); + } + } + else + RTMsgError(Appliance::tr("RTCrPkcs7ContentInfo_DecodeAsn1 failed to decode PKCS#7/CMS signature: %Rrc%RTemi"), + vrc, &pErrInfo->Core); + + RTCrPkcs7ContentInfo_Delete(&ContentInfo); + return vrc; +} + + +/** + * Creates a PKCS\#7 signature and appends it to the signature file in PEM + * format. + */ +static int doAddPkcs7Signature(PCRTCRX509CERTIFICATE pCertificate, RTCRKEY hPrivateKey, RTDIGESTTYPE enmDigestType, + unsigned cIntermediateCerts, const char **papszIntermediateCerts, RTVFSFILE hVfsFileManifest, + unsigned iVerbosity, PRTERRINFOSTATIC pErrInfo, RTVFSFILE hVfsFileSignature) +{ + /* + * Add a blank line, just for good measure. + */ + int vrc = RTVfsFileWrite(hVfsFileSignature, "\n", 1, NULL); + if (RT_FAILURE(vrc)) + return RTMsgErrorRc(vrc, "RTVfsFileWrite/signature: %Rrc", vrc); + + /* + * Read the manifest into a single memory block. + */ + uint64_t cbManifest; + vrc = RTVfsFileQuerySize(hVfsFileManifest, &cbManifest); + if (RT_FAILURE(vrc)) + return RTMsgErrorRc(vrc, "RTVfsFileQuerySize/manifest: %Rrc", vrc); + if (cbManifest > _4M) + return RTMsgErrorRc(VERR_OUT_OF_RANGE, Appliance::tr("Manifest is too big: %#RX64 bytes, max 4MiB", "", cbManifest), + cbManifest); + + void *pvManifest = RTMemAllocZ(cbManifest + 1); + if (!pvManifest) + return RTMsgErrorRc(VERR_NO_MEMORY, Appliance::tr("Out of memory!")); + + vrc = RTVfsFileReadAt(hVfsFileManifest, 0, pvManifest, (size_t)cbManifest, NULL); + if (RT_SUCCESS(vrc)) + { + /* + * Load intermediate certificates. + */ + RTCRSTORE hIntermediateCerts = NIL_RTCRSTORE; + if (cIntermediateCerts) + { + vrc = RTCrStoreCreateInMem(&hIntermediateCerts, cIntermediateCerts); + if (RT_SUCCESS(vrc)) + { + for (unsigned i = 0; i < cIntermediateCerts; i++) + { + const char *pszFile = papszIntermediateCerts[i]; + vrc = RTCrStoreCertAddFromFile(hIntermediateCerts, 0 /*fFlags*/, pszFile, &pErrInfo->Core); + if (RT_FAILURE(vrc)) + { + RTMsgError(Appliance::tr("RTCrStoreCertAddFromFile failed on '%s': %Rrc%#RTeim"), + pszFile, vrc, &pErrInfo->Core); + break; + } + } + } + else + RTMsgError(Appliance::tr("RTCrStoreCreateInMem failed: %Rrc"), vrc); + } + if (RT_SUCCESS(vrc)) + { + /* + * Do a dry run to determin the size of the signed data. + */ + size_t cbResult = 0; + vrc = RTCrPkcs7SimpleSignSignedData(RTCRPKCS7SIGN_SD_F_DEATCHED | RTCRPKCS7SIGN_SD_F_NO_SMIME_CAP, + pCertificate, hPrivateKey, pvManifest, (size_t)cbManifest, enmDigestType, + hIntermediateCerts, NULL /*pAdditionalAuthenticatedAttribs*/, + NULL /*pvResult*/, &cbResult, RTErrInfoInitStatic(pErrInfo)); + if (vrc == VERR_BUFFER_OVERFLOW) + { + /* + * Allocate a buffer of the right size and do the real run. + */ + void *pvResult = RTMemAllocZ(cbResult); + if (pvResult) + { + vrc = RTCrPkcs7SimpleSignSignedData(RTCRPKCS7SIGN_SD_F_DEATCHED | RTCRPKCS7SIGN_SD_F_NO_SMIME_CAP, + pCertificate, hPrivateKey, pvManifest, (size_t)cbManifest, enmDigestType, + hIntermediateCerts, NULL /*pAdditionalAuthenticatedAttribs*/, + pvResult, &cbResult, RTErrInfoInitStatic(pErrInfo)); + if (RT_SUCCESS(vrc)) + { + /* + * Add it to the signature file in PEM format. + */ + vrc = (int)RTCrPemWriteBlobToVfsFile(hVfsFileSignature, pvResult, cbResult, "CMS"); + if (RT_SUCCESS(vrc)) + { + if (iVerbosity > 1) + RTMsgInfo(Appliance::tr("Created PKCS#7/CMS signature: %zu bytes, %s.", "", cbResult), + cbResult, RTCrDigestTypeToName(enmDigestType)); + if (enmDigestType == RTDIGESTTYPE_SHA1) + RTMsgWarning(Appliance::tr("Using SHA-1 instead of SHA-3 for the PKCS#7/CMS signature.")); + + /* + * Try decode and verify the signature. + */ + vrc = doCheckPkcs7Signature(pvResult, cbResult, pCertificate, hIntermediateCerts, + pvManifest, (size_t)cbManifest, iVerbosity, pErrInfo); + } + else + RTMsgError(Appliance::tr("RTCrPemWriteBlobToVfsFile failed: %Rrc"), vrc); + } + RTMemFree(pvResult); + } + else + vrc = RTMsgErrorRc(VERR_NO_MEMORY, Appliance::tr("Out of memory!")); + } + else + RTMsgError(Appliance::tr("RTCrPkcs7SimpleSignSignedData failed: %Rrc%#RTeim"), vrc, &pErrInfo->Core); + } + } + else + RTMsgError(Appliance::tr("RTVfsFileReadAt failed: %Rrc"), vrc); + RTMemFree(pvManifest); + return vrc; +} + + +/** + * Performs the OVA signing, producing an in-memory cert-file. + */ +static int doTheOvaSigning(PRTCRX509CERTIFICATE pCertificate, RTCRKEY hPrivateKey, RTDIGESTTYPE enmDigestType, + const char *pszManifestName, RTVFSFILE hVfsFileManifest, + bool fPkcs7, unsigned cIntermediateCerts, const char **papszIntermediateCerts, unsigned iVerbosity, + PRTERRINFOSTATIC pErrInfo, PRTVFSFILE phVfsFileSignature) +{ + /* + * Determine the digest types, preferring SHA-256 for the OVA signature + * and SHA-512 for the PKCS#7/CMS one. Try use different hashes for the two. + */ + if (enmDigestType == RTDIGESTTYPE_UNKNOWN) + { + if (RTCrPkixCanCertHandleDigestType(pCertificate, RTDIGESTTYPE_SHA256, NULL)) + enmDigestType = RTDIGESTTYPE_SHA256; + else + enmDigestType = RTDIGESTTYPE_SHA1; + } + + /* Try SHA-3 for better diversity, only fall back on SHA1 if the private + key doesn't have enough bits (we skip SHA2 as it has the same variants + and key size requirements as SHA-3). */ + RTDIGESTTYPE enmPkcs7DigestType; + if (RTCrPkixCanCertHandleDigestType(pCertificate, RTDIGESTTYPE_SHA3_512, NULL)) + enmPkcs7DigestType = RTDIGESTTYPE_SHA3_512; + else if (RTCrPkixCanCertHandleDigestType(pCertificate, RTDIGESTTYPE_SHA3_384, NULL)) + enmPkcs7DigestType = RTDIGESTTYPE_SHA3_384; + else if (RTCrPkixCanCertHandleDigestType(pCertificate, RTDIGESTTYPE_SHA3_256, NULL)) + enmPkcs7DigestType = RTDIGESTTYPE_SHA3_256; + else if (RTCrPkixCanCertHandleDigestType(pCertificate, RTDIGESTTYPE_SHA3_224, NULL)) + enmPkcs7DigestType = RTDIGESTTYPE_SHA3_224; + else + enmPkcs7DigestType = RTDIGESTTYPE_SHA1; + + /* + * Figure the string name for the .cert file. + */ + const char *pszDigestType; + switch (enmDigestType) + { + case RTDIGESTTYPE_SHA1: pszDigestType = "SHA1"; break; + case RTDIGESTTYPE_SHA256: pszDigestType = "SHA256"; break; + case RTDIGESTTYPE_SHA224: pszDigestType = "SHA224"; break; + case RTDIGESTTYPE_SHA512: pszDigestType = "SHA512"; break; + default: + return RTMsgErrorRc(VERR_INVALID_PARAMETER, + Appliance::tr("Unsupported digest type: %s"), RTCrDigestTypeToName(enmDigestType)); + } + + /* + * Digest the manifest file. + */ + RTCRDIGEST hDigest = NIL_RTCRDIGEST; + int vrc = RTCrDigestCreateByType(&hDigest, enmDigestType); + if (RT_FAILURE(vrc)) + return RTMsgErrorRc(vrc, Appliance::tr("Failed to create digest for %s: %Rrc"), RTCrDigestTypeToName(enmDigestType), vrc); + + vrc = RTCrDigestUpdateFromVfsFile(hDigest, hVfsFileManifest, true /*fRewindFile*/); + if (RT_SUCCESS(vrc)) + vrc = RTCrDigestFinal(hDigest, NULL, 0); + if (RT_SUCCESS(vrc)) + { + /* + * Sign the digest. Two passes, first to figure the signature size, the + * second to do the actual signing. + */ + PCRTASN1OBJID const pAlgorithm = &pCertificate->TbsCertificate.SubjectPublicKeyInfo.Algorithm.Algorithm; + PCRTASN1DYNTYPE const pAlgoParams = &pCertificate->TbsCertificate.SubjectPublicKeyInfo.Algorithm.Parameters; + size_t cbSignature = 0; + vrc = RTCrPkixPubKeySignDigest(pAlgorithm, hPrivateKey, pAlgoParams, hDigest, 0 /*fFlags*/, + NULL /*pvSignature*/, &cbSignature, RTErrInfoInitStatic(pErrInfo)); + if (vrc == VERR_BUFFER_OVERFLOW) + { + void *pvSignature = RTMemAllocZ(cbSignature); + if (pvSignature) + { + vrc = RTCrPkixPubKeySignDigest(pAlgorithm, hPrivateKey, pAlgoParams, hDigest, 0, + pvSignature, &cbSignature, RTErrInfoInitStatic(pErrInfo)); + if (RT_SUCCESS(vrc)) + { + if (iVerbosity > 1) + RTMsgInfo(Appliance::tr("Created OVA signature: %zu bytes, %s", "", cbSignature), cbSignature, + RTCrDigestTypeToName(enmDigestType)); + + /* + * Verify the signature using the certificate to make sure we've + * been given the right private key. + */ + vrc = RTCrPkixPubKeyVerifySignedDigestByCertPubKeyInfo(&pCertificate->TbsCertificate.SubjectPublicKeyInfo, + pvSignature, cbSignature, hDigest, + RTErrInfoInitStatic(pErrInfo)); + if (RT_SUCCESS(vrc)) + { + if (iVerbosity > 2) + RTMsgInfo(Appliance::tr(" Successfully decoded and verified the OVA signature.\n")); + + /* + * Create the output file. + */ + RTVFSFILE hVfsFileSignature; + vrc = RTVfsMemFileCreate(NIL_RTVFSIOSTREAM, _8K, &hVfsFileSignature); + if (RT_SUCCESS(vrc)) + { + vrc = (int)RTVfsFilePrintf(hVfsFileSignature, "%s(%s) = %#.*Rhxs\n\n", + pszDigestType, pszManifestName, cbSignature, pvSignature); + if (RT_SUCCESS(vrc)) + { + vrc = (int)RTCrX509Certificate_WriteToVfsFile(hVfsFileSignature, pCertificate, + RTErrInfoInitStatic(pErrInfo)); + if (RT_SUCCESS(vrc)) + { + if (fPkcs7) + vrc = doAddPkcs7Signature(pCertificate, hPrivateKey, enmPkcs7DigestType, + cIntermediateCerts, papszIntermediateCerts, hVfsFileManifest, + iVerbosity, pErrInfo, hVfsFileSignature); + if (RT_SUCCESS(vrc)) + { + /* + * Success. + */ + *phVfsFileSignature = hVfsFileSignature; + hVfsFileSignature = NIL_RTVFSFILE; + } + } + else + RTMsgError(Appliance::tr("Failed to write certificate to signature file: %Rrc%#RTeim"), + vrc, &pErrInfo->Core); + } + else + RTMsgError(Appliance::tr("Failed to produce signature file: %Rrc"), vrc); + RTVfsFileRelease(hVfsFileSignature); + } + else + RTMsgError(Appliance::tr("RTVfsMemFileCreate failed: %Rrc"), vrc); + } + else + RTMsgError(Appliance::tr("Encountered a problem when validating the signature we just created: %Rrc%#RTeim\n" + "Please make sure the certificate and private key matches."), + vrc, &pErrInfo->Core); + } + else + RTMsgError(Appliance::tr("2nd RTCrPkixPubKeySignDigest call failed: %Rrc%#RTeim"), vrc, pErrInfo->Core); + RTMemFree(pvSignature); + } + else + vrc = RTMsgErrorRc(VERR_NO_MEMORY, Appliance::tr("Out of memory!")); + } + else + RTMsgError(Appliance::tr("RTCrPkixPubKeySignDigest failed: %Rrc%#RTeim"), vrc, pErrInfo->Core); + } + else + RTMsgError(Appliance::tr("Failed to create digest %s: %Rrc"), RTCrDigestTypeToName(enmDigestType), vrc); + RTCrDigestRelease(hDigest); + return vrc; +} + + +/** + * Handles the 'ovasign' command. + */ +RTEXITCODE handleSignAppliance(HandlerArg *arg) +{ + /* + * Parse arguments. + */ + static const RTGETOPTDEF s_aOptions[] = + { + { "--certificate", 'c', RTGETOPT_REQ_STRING }, + { "--private-key", 'k', RTGETOPT_REQ_STRING }, + { "--private-key-password", 'p', RTGETOPT_REQ_STRING }, + { "--private-key-password-file",'P', RTGETOPT_REQ_STRING }, + { "--digest-type", 'd', RTGETOPT_REQ_STRING }, + { "--pkcs7", '7', RTGETOPT_REQ_NOTHING }, + { "--cms", '7', RTGETOPT_REQ_NOTHING }, + { "--no-pkcs7", 'n', RTGETOPT_REQ_NOTHING }, + { "--no-cms", 'n', RTGETOPT_REQ_NOTHING }, + { "--intermediate-cert-file", 'i', RTGETOPT_REQ_STRING }, + { "--force", 'f', RTGETOPT_REQ_NOTHING }, + { "--verbose", 'v', RTGETOPT_REQ_NOTHING }, + { "--quiet", 'q', RTGETOPT_REQ_NOTHING }, + { "--dry-run", 'D', RTGETOPT_REQ_NOTHING }, + }; + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, arg->argc, arg->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + const char *pszOva = NULL; + const char *pszCertificate = NULL; + const char *pszPrivateKey = NULL; + Utf8Str strPrivateKeyPassword; + RTDIGESTTYPE enmDigestType = RTDIGESTTYPE_UNKNOWN; + bool fPkcs7 = true; + unsigned cIntermediateCerts = 0; + const char *apszIntermediateCerts[32]; + bool fReSign = false; + unsigned iVerbosity = 1; + bool fDryRun = false; + + int c; + RTGETOPTUNION ValueUnion; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'c': + pszCertificate = ValueUnion.psz; + break; + + case 'k': + pszPrivateKey = ValueUnion.psz; + break; + + case 'p': + if (strPrivateKeyPassword.isNotEmpty()) + RTMsgWarning(Appliance::tr("Password is given more than once.")); + strPrivateKeyPassword = ValueUnion.psz; + break; + + case 'P': + { + if (strPrivateKeyPassword.isNotEmpty()) + RTMsgWarning(Appliance::tr("Password is given more than once.")); + RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPrivateKeyPassword); + if (rcExit == RTEXITCODE_SUCCESS) + break; + return rcExit; + } + + case 'd': + if ( RTStrICmp(ValueUnion.psz, "sha1") == 0 + || RTStrICmp(ValueUnion.psz, "sha-1") == 0) + enmDigestType = RTDIGESTTYPE_SHA1; + else if ( RTStrICmp(ValueUnion.psz, "sha256") == 0 + || RTStrICmp(ValueUnion.psz, "sha-256") == 0) + enmDigestType = RTDIGESTTYPE_SHA256; + else if ( RTStrICmp(ValueUnion.psz, "sha512") == 0 + || RTStrICmp(ValueUnion.psz, "sha-512") == 0) + enmDigestType = RTDIGESTTYPE_SHA512; + else + return RTMsgErrorExitFailure(Appliance::tr("Unknown digest type: %s"), ValueUnion.psz); + break; + + case '7': + fPkcs7 = true; + break; + + case 'n': + fPkcs7 = false; + break; + + case 'i': + if (cIntermediateCerts >= RT_ELEMENTS(apszIntermediateCerts)) + return RTMsgErrorExitFailure(Appliance::tr("Too many intermediate certificates: max %zu"), + RT_ELEMENTS(apszIntermediateCerts)); + apszIntermediateCerts[cIntermediateCerts++] = ValueUnion.psz; + fPkcs7 = true; + break; + + case 'f': + fReSign = true; + break; + + case 'v': + iVerbosity++; + break; + + case 'q': + iVerbosity = 0; + break; + + case 'D': + fDryRun = true; + break; + + case VINF_GETOPT_NOT_OPTION: + if (!pszOva) + { + pszOva = ValueUnion.psz; + break; + } + RT_FALL_THRU(); + default: + return errorGetOpt(c, &ValueUnion); + } + } + + /* Required paramaters: */ + if (!pszOva || !*pszOva) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, Appliance::tr("No OVA file was specified!")); + if (!pszCertificate || !*pszCertificate) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, Appliance::tr("No signing certificate (--certificate=) was specified!")); + if (!pszPrivateKey || !*pszPrivateKey) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, Appliance::tr("No signing private key (--private-key=) was specified!")); + + /* Check that input files exists before we commence: */ + if (!RTFileExists(pszOva)) + return RTMsgErrorExitFailure(Appliance::tr("The specified OVA file was not found: %s"), pszOva); + if (!RTFileExists(pszCertificate)) + return RTMsgErrorExitFailure(Appliance::tr("The specified certificate file was not found: %s"), pszCertificate); + if (!RTFileExists(pszPrivateKey)) + return RTMsgErrorExitFailure(Appliance::tr("The specified private key file was not found: %s"), pszPrivateKey); + + /* + * Open the OVA, read the manifest and look for any existing signature. + */ + RTVFSFSSTREAM hVfsFssOva = NIL_RTVFSFSSTREAM; + RTVFSOBJ hVfsOldSignature = NIL_RTVFSOBJ; + RTVFSFILE hVfsFileManifest = NIL_RTVFSFILE; + Utf8Str strManifestName; + vrc = openOvaAndGetManifestAndOldSignature(pszOva, iVerbosity, fReSign, + &hVfsFssOva, &strManifestName, &hVfsFileManifest, &hVfsOldSignature); + if (RT_SUCCESS(vrc)) + { + /* + * Read the certificate and private key. + */ + RTERRINFOSTATIC ErrInfo; + RTCRX509CERTIFICATE Certificate; + vrc = RTCrX509Certificate_ReadFromFile(&Certificate, pszCertificate, 0, &g_RTAsn1DefaultAllocator, + RTErrInfoInitStatic(&ErrInfo)); + if (RT_FAILURE(vrc)) + return RTMsgErrorExitFailure(Appliance::tr("Error reading certificate from '%s': %Rrc%#RTeim"), + pszCertificate, vrc, &ErrInfo.Core); + + RTCRKEY hPrivateKey = NIL_RTCRKEY; + vrc = RTCrKeyCreateFromFile(&hPrivateKey, 0 /*fFlags*/, pszPrivateKey, strPrivateKeyPassword.c_str(), + RTErrInfoInitStatic(&ErrInfo)); + if (RT_SUCCESS(vrc)) + { + if (iVerbosity > 1) + RTMsgInfo(Appliance::tr("Successfully read the certificate and private key.")); + + /* + * Do the signing and create the signature file. + */ + RTVFSFILE hVfsFileSignature = NIL_RTVFSFILE; + vrc = doTheOvaSigning(&Certificate, hPrivateKey, enmDigestType, strManifestName.c_str(), hVfsFileManifest, + fPkcs7, cIntermediateCerts, apszIntermediateCerts, iVerbosity, &ErrInfo, &hVfsFileSignature); + + /* + * Construct the signature filename: + */ + if (RT_SUCCESS(vrc)) + { + Utf8Str strSignatureName; + vrc = strSignatureName.assignNoThrow(strManifestName); + if (RT_SUCCESS(vrc)) + vrc = strSignatureName.stripSuffix().appendNoThrow(".cert"); + if (RT_SUCCESS(vrc) && !fDryRun) + { + /* + * Update the OVA. + */ + vrc = updateTheOvaSignature(hVfsFssOva, pszOva, strSignatureName.c_str(), + hVfsFileSignature, hVfsOldSignature, iVerbosity); + if (RT_SUCCESS(vrc) && iVerbosity > 0) + RTMsgInfo(Appliance::tr("Successfully signed '%s'."), pszOva); + } + } + RTCrKeyRelease(hPrivateKey); + } + else + RTPrintf(Appliance::tr("Error reading the private key from %s: %Rrc%#RTeim"), pszPrivateKey, vrc, &ErrInfo.Core); + RTCrX509Certificate_Delete(&Certificate); + } + + RTVfsObjRelease(hVfsOldSignature); + RTVfsFileRelease(hVfsFileManifest); + RTVfsFsStrmRelease(hVfsFssOva); + + return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageBandwidthControl.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageBandwidthControl.cpp new file mode 100644 index 00000000..910f5ad5 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageBandwidthControl.cpp @@ -0,0 +1,384 @@ +/* $Id: VBoxManageBandwidthControl.cpp $ */ +/** @file + * VBoxManage - The bandwidth control related commands. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "VBoxManage.h" +using namespace com; + +DECLARE_TRANSLATION_CONTEXT(BWControl); + +// 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 vrc = RTStrToInt64Ex(pcszLimit, &pszNext, 10, pLimit); + + switch (vrc) + { + case VINF_SUCCESS: + break; + case VWRN_NUMBER_TOO_BIG: + return BWControl::tr("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 BWControl::tr("Invalid unit suffix. Valid suffixes are: k, m, g, K, M, G\n"); + } + break; + case VWRN_TRAILING_SPACES: + return BWControl::tr("Trailing spaces in limit!\n"); + case VERR_NO_DIGITS: + return BWControl::tr("No digits in limit specifier\n"); + default: + return BWControl::tr("Invalid limit specifier\n"); + } + if (*pLimit < 0) + return BWControl::tr("Limit cannot be negative\n"); + if (*pLimit > INT64_MAX / iMultiplier) + return BWControl::tr("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 &bwCtrl) +{ + HRESULT hrc = S_OK; + static const RTGETOPTDEF g_aBWCtlAddOptions[] = + { + { "--type", 't', RTGETOPT_REQ_STRING }, + { "--limit", 'l', RTGETOPT_REQ_STRING } + }; + + setCurrentSubcommand(HELP_SCOPE_BANDWIDTHCTL_ADD); + + Bstr name(a->argv[2]); + if (name.isEmpty()) + { + errorArgument(BWControl::tr("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(hrc) + && (c = RTGetOpt(&GetState, &ValueUnion))) + { + switch (c) + { + case 't': // bandwidth group type + { + if (ValueUnion.psz) + pszType = ValueUnion.psz; + else + hrc = E_FAIL; + break; + } + + case 'l': // limit + { + if (ValueUnion.psz) + { + const char *pcszError = parseLimit(ValueUnion.psz, &cMaxBytesPerSec); + if (pcszError) + { + errorArgument(pcszError); + return RTEXITCODE_FAILURE; + } + } + else + hrc = E_FAIL; + break; + } + + default: + { + errorGetOpt(c, &ValueUnion); + hrc = E_FAIL; + break; + } + } + } + + BandwidthGroupType_T enmType; + + if (!RTStrICmp(pszType, "disk")) + enmType = BandwidthGroupType_Disk; + else if (!RTStrICmp(pszType, "network")) + enmType = BandwidthGroupType_Network; + else + { + errorArgument(BWControl::tr("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 &bwCtrl) +{ + HRESULT hrc = S_OK; + static const RTGETOPTDEF g_aBWCtlAddOptions[] = + { + { "--limit", 'l', RTGETOPT_REQ_STRING } + }; + + setCurrentSubcommand(HELP_SCOPE_BANDWIDTHCTL_SET); + + 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(hrc) + && (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 + hrc = E_FAIL; + break; + } + + default: + { + errorGetOpt(c, &ValueUnion); + hrc = E_FAIL; + break; + } + } + } + + + if (cMaxBytesPerSec != INT64_MAX) + { + ComPtr bwGroup; + CHECK_ERROR2I_RET(bwCtrl, GetBandwidthGroup(name.raw(), bwGroup.asOutParam()), RTEXITCODE_FAILURE); + if (SUCCEEDED(hrc)) + { + 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 &bwCtrl) +{ + setCurrentSubcommand(HELP_SCOPE_BANDWIDTHCTL_REMOVE); + + 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 &rptrBWControl) +{ + static const RTGETOPTDEF g_aOptions[] = + { + { "--machinereadable", 'M', RTGETOPT_REQ_NOTHING }, + }; + + setCurrentSubcommand(HELP_SCOPE_BANDWIDTHCTL_LIST); + 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(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 hrc = S_OK; + ComPtr machine; + ComPtr bwCtrl; + + if (a->argc < 2) + return errorSyntax(BWControl::tr("Too few parameters")); + else if (a->argc > 7) + return errorSyntax(BWControl::tr("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()); + hrc = machine->COMGETTER(BandwidthControl)(bwCtrl.asOutParam()); + if (FAILED(hrc)) goto leave; /** @todo r=andy Argh!! */ + + if (!strcmp(a->argv[1], "add")) + { + if (fRunTime) + { + errorArgument(BWControl::tr("Bandwidth groups cannot be created while the VM is running\n")); + goto leave; + } + hrc = handleBandwidthControlAdd(a, bwCtrl) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL; + } + else if (!strcmp(a->argv[1], "remove")) + { + if (fRunTime) + { + errorArgument(BWControl::tr("Bandwidth groups cannot be deleted while the VM is running\n")); + goto leave; + } + hrc = handleBandwidthControlRemove(a, bwCtrl) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL; + } + else if (!strcmp(a->argv[1], "set")) + hrc = handleBandwidthControlSet(a, bwCtrl) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL; + else if (!strcmp(a->argv[1], "list")) + hrc = handleBandwidthControlList(a, bwCtrl) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL; + else + { + errorSyntax(BWControl::tr("Invalid parameter '%s'"), a->argv[1]); + hrc = E_FAIL; + } + + /* commit changes */ + if (SUCCEEDED(hrc)) + CHECK_ERROR(machine, SaveSettings()); + +leave: + /* it's important to always close sessions */ + a->session->UnlockMachine(); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageCloud.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageCloud.cpp new file mode 100644 index 00000000..690cb7d9 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageCloud.cpp @@ -0,0 +1,2654 @@ +/* $Id: VBoxManageCloud.cpp $ */ +/** @file + * VBoxManageCloud - The cloud related commands. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "VBoxManage.h" + +#include + +using namespace com;//at least for Bstr + +DECLARE_TRANSLATION_CONTEXT(Cloud); + + +/** + * Common Cloud options. + */ +typedef struct +{ + struct { + const char *pszProviderName; + ComPtr pCloudProvider; + }provider; + struct { + const char *pszProfileName; + ComPtr pCloudProfile; + }profile; + +} CLOUDCOMMONOPT; +typedef CLOUDCOMMONOPT *PCLOUDCOMMONOPT; + +static HRESULT checkAndSetCommonOptions(HandlerArg *a, PCLOUDCOMMONOPT pCommonOpts) +{ + HRESULT hrc = S_OK; + + Bstr bstrProvider(pCommonOpts->provider.pszProviderName); + Bstr bstrProfile(pCommonOpts->profile.pszProfileName); + + /* check for required options */ + if (bstrProvider.isEmpty()) + { + errorSyntax(Cloud::tr("Parameter --provider is required")); + return E_FAIL; + } + if (bstrProfile.isEmpty()) + { + errorSyntax(Cloud::tr("Parameter --profile is required")); + return E_FAIL; + } + + ComPtr pVirtualBox = a->virtualBox; + ComPtr pCloudProviderManager; + CHECK_ERROR2_RET(hrc, pVirtualBox, + COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()), + RTEXITCODE_FAILURE); + + ComPtr pCloudProvider; + CHECK_ERROR2_RET(hrc, pCloudProviderManager, + GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()), + RTEXITCODE_FAILURE); + pCommonOpts->provider.pCloudProvider = pCloudProvider; + + ComPtr pCloudProfile; + CHECK_ERROR2_RET(hrc, pCloudProvider, + GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()), + RTEXITCODE_FAILURE); + pCommonOpts->profile.pCloudProfile = pCloudProfile; + + return hrc; +} + + +/** + * List all available cloud instances for the specified cloud provider. + * Available cloud instance is one which state whether "running" or "stopped". + * + * @returns RTEXITCODE + * @param a is the list of passed arguments + * @param iFirst is the position of the first unparsed argument in the arguments list + * @param pCommonOpts is a pointer to the structure CLOUDCOMMONOPT with some common + * arguments which have been already parsed before + */ +static RTEXITCODE listCloudInstances(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + static const RTGETOPTDEF s_aOptions[] = + { + { "--compartment-id", 'c', RTGETOPT_REQ_STRING }, + { "--state", 's', RTGETOPT_REQ_STRING }, + { "help", 'h', RTGETOPT_REQ_NOTHING }, + { "--help", 'h', RTGETOPT_REQ_NOTHING } + }; + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + Utf8Str strCompartmentId; + com::SafeArray machineStates; + + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'c': + strCompartmentId = ValueUnion.psz; + break; + + case 's': + { + const char * const pszState = ValueUnion.psz; + + if (RTStrICmp(pszState, "creatingimage") == 0) + machineStates.push_back(CloudMachineState_CreatingImage); + else if (RTStrICmp(pszState, "paused") == 0) /* XXX */ + machineStates.push_back(CloudMachineState_Stopped); + else if (RTStrICmp(pszState, "provisioning") == 0) + machineStates.push_back(CloudMachineState_Provisioning); + else if (RTStrICmp(pszState, "running") == 0) + machineStates.push_back(CloudMachineState_Running); + else if (RTStrICmp(pszState, "starting") == 0) + machineStates.push_back(CloudMachineState_Starting); + else if (RTStrICmp(pszState, "stopped") == 0) + machineStates.push_back(CloudMachineState_Stopped); + else if (RTStrICmp(pszState, "stopping") == 0) + machineStates.push_back(CloudMachineState_Stopping); + else if (RTStrICmp(pszState, "terminated") == 0) + machineStates.push_back(CloudMachineState_Terminated); + else if (RTStrICmp(pszState, "terminating") == 0) + machineStates.push_back(CloudMachineState_Terminating); + else + return errorArgument(Cloud::tr("Unknown cloud instance state \"%s\""), pszState); + break; + } + case 'h': + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + HRESULT hrc = S_OK; + + /* Delayed check. It allows us to print help information.*/ + hrc = checkAndSetCommonOptions(a, pCommonOpts); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + ComPtr pVirtualBox = a->virtualBox; + + ComPtr pCloudProviderManager; + CHECK_ERROR2_RET(hrc, pVirtualBox, + COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()), + RTEXITCODE_FAILURE); + + ComPtr pCloudProvider; + CHECK_ERROR2_RET(hrc, pCloudProviderManager, + GetProviderByShortName(Bstr(pCommonOpts->provider.pszProviderName).raw(), pCloudProvider.asOutParam()), + RTEXITCODE_FAILURE); + + ComPtr pCloudProfile; + CHECK_ERROR2_RET(hrc, pCloudProvider, + GetProfileByName(Bstr(pCommonOpts->profile.pszProfileName).raw(), pCloudProfile.asOutParam()), + RTEXITCODE_FAILURE); + + if (strCompartmentId.isNotEmpty()) + { + CHECK_ERROR2_RET(hrc, pCloudProfile, + SetProperty(Bstr("compartment").raw(), Bstr(strCompartmentId).raw()), + RTEXITCODE_FAILURE); + } + else + { + RTPrintf(Cloud::tr("Parameter \'compartment\' is empty or absent.\n" + "Trying to get the compartment from the passed cloud profile \'%s\'\n"), + pCommonOpts->profile.pszProfileName); + Bstr bStrCompartmentId; + CHECK_ERROR2_RET(hrc, pCloudProfile, + GetProperty(Bstr("compartment").raw(), bStrCompartmentId.asOutParam()), + RTEXITCODE_FAILURE); + strCompartmentId = bStrCompartmentId; + if (strCompartmentId.isNotEmpty()) + RTPrintf(Cloud::tr("Found the compartment \'%s\':\n"), strCompartmentId.c_str()); + else + return errorSyntax(Cloud::tr("Parameter --compartment-id is required")); + } + + Bstr bstrProfileName; + pCloudProfile->COMGETTER(Name)(bstrProfileName.asOutParam()); + + ComObjPtr oCloudClient; + CHECK_ERROR2_RET(hrc, pCloudProfile, + CreateCloudClient(oCloudClient.asOutParam()), + RTEXITCODE_FAILURE); + + ComPtr pVMNamesHolder; + ComPtr pVMIdsHolder; + com::SafeArray arrayVMNames; + com::SafeArray arrayVMIds; + ComPtr pProgress; + + RTPrintf(Cloud::tr("Reply is in the form \'instance name\' = \'instance id\'\n")); + + CHECK_ERROR2_RET(hrc, oCloudClient, + ListInstances(ComSafeArrayAsInParam(machineStates), + pVMNamesHolder.asOutParam(), + pVMIdsHolder.asOutParam(), + pProgress.asOutParam()), + RTEXITCODE_FAILURE); + showProgress(pProgress); + CHECK_PROGRESS_ERROR_RET(pProgress, (Cloud::tr("Failed to list instances")), RTEXITCODE_FAILURE); + + CHECK_ERROR2_RET(hrc, + pVMNamesHolder, COMGETTER(Values)(ComSafeArrayAsOutParam(arrayVMNames)), + RTEXITCODE_FAILURE); + CHECK_ERROR2_RET(hrc, + pVMIdsHolder, COMGETTER(Values)(ComSafeArrayAsOutParam(arrayVMIds)), + RTEXITCODE_FAILURE); + + RTPrintf(Cloud::tr("The list of the instances for the cloud profile \'%ls\'\nand compartment \'%s\':\n"), + bstrProfileName.raw(), strCompartmentId.c_str()); + size_t cIds = arrayVMIds.size(); + size_t cNames = arrayVMNames.size(); + for (size_t k = 0; k < cNames; k++) + { + Bstr value; + if (k < cIds) + value = arrayVMIds[k]; + RTPrintf("\t%ls = %ls\n", arrayVMNames[k], value.raw()); + } + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + + +/** + * List all available cloud images for the specified cloud provider. + * + * @returns RTEXITCODE + * @param a is the list of passed arguments + * @param iFirst is the position of the first unparsed argument in the arguments list + * @param pCommonOpts is a pointer to the structure CLOUDCOMMONOPT with some common + * arguments which have been already parsed before + */ +static RTEXITCODE listCloudImages(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + static const RTGETOPTDEF s_aOptions[] = + { + { "--compartment-id", 'c', RTGETOPT_REQ_STRING }, + { "--state", 's', RTGETOPT_REQ_STRING }, + { "help", 'h', RTGETOPT_REQ_NOTHING }, + { "--help", 'h', RTGETOPT_REQ_NOTHING } + }; + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + Utf8Str strCompartmentId; + com::SafeArray imageStates; + + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'c': + strCompartmentId = ValueUnion.psz; + break; + + case 's': + { + const char * const pszState = ValueUnion.psz; + + if (RTStrICmp(pszState, "available") == 0) + imageStates.push_back(CloudImageState_Available); + else if (RTStrICmp(pszState, "deleted") == 0) + imageStates.push_back(CloudImageState_Deleted); + else if (RTStrICmp(pszState, "disabled") == 0) + imageStates.push_back(CloudImageState_Disabled); + else if (RTStrICmp(pszState, "exporting") == 0) + imageStates.push_back(CloudImageState_Exporting); + else if (RTStrICmp(pszState, "importing") == 0) + imageStates.push_back(CloudImageState_Importing); + else if (RTStrICmp(pszState, "provisioning") == 0) + imageStates.push_back(CloudImageState_Provisioning); + else + return errorArgument(Cloud::tr("Unknown cloud image state \"%s\""), pszState); + break; + } + case 'h': + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + + HRESULT hrc = S_OK; + + /* Delayed check. It allows us to print help information.*/ + hrc = checkAndSetCommonOptions(a, pCommonOpts); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + ComPtr pVirtualBox = a->virtualBox; + + ComPtr pCloudProviderManager; + CHECK_ERROR2_RET(hrc, pVirtualBox, + COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()), + RTEXITCODE_FAILURE); + + ComPtr pCloudProvider; + CHECK_ERROR2_RET(hrc, pCloudProviderManager, + GetProviderByShortName(Bstr(pCommonOpts->provider.pszProviderName).raw(), pCloudProvider.asOutParam()), + RTEXITCODE_FAILURE); + + ComPtr pCloudProfile; + CHECK_ERROR2_RET(hrc, pCloudProvider, + GetProfileByName(Bstr(pCommonOpts->profile.pszProfileName).raw(), pCloudProfile.asOutParam()), + RTEXITCODE_FAILURE); + + if (strCompartmentId.isNotEmpty()) + { + CHECK_ERROR2_RET(hrc, pCloudProfile, + SetProperty(Bstr("compartment").raw(), Bstr(strCompartmentId).raw()),\ + RTEXITCODE_FAILURE); + } + else + { + RTPrintf(Cloud::tr("Parameter \'compartment\' is empty or absent.\n" + "Trying to get the compartment from the passed cloud profile \'%s\'\n"), + pCommonOpts->profile.pszProfileName); + Bstr bStrCompartmentId; + CHECK_ERROR2_RET(hrc, pCloudProfile, + GetProperty(Bstr("compartment").raw(), bStrCompartmentId.asOutParam()), + RTEXITCODE_FAILURE); + strCompartmentId = bStrCompartmentId; + if (strCompartmentId.isNotEmpty()) + RTPrintf(Cloud::tr("Found the compartment \'%s\':\n"), strCompartmentId.c_str()); + else + return errorSyntax(Cloud::tr("Parameter --compartment-id is required")); + } + + Bstr bstrProfileName; + pCloudProfile->COMGETTER(Name)(bstrProfileName.asOutParam()); + + ComObjPtr oCloudClient; + CHECK_ERROR2_RET(hrc, pCloudProfile, + CreateCloudClient(oCloudClient.asOutParam()), + RTEXITCODE_FAILURE); + + ComPtr pVMNamesHolder; + ComPtr pVMIdsHolder; + com::SafeArray arrayVMNames; + com::SafeArray arrayVMIds; + ComPtr pProgress; + + RTPrintf(Cloud::tr("Reply is in the form \'image name\' = \'image id\'\n")); + CHECK_ERROR2_RET(hrc, oCloudClient, + ListImages(ComSafeArrayAsInParam(imageStates), + pVMNamesHolder.asOutParam(), + pVMIdsHolder.asOutParam(), + pProgress.asOutParam()), + RTEXITCODE_FAILURE); + showProgress(pProgress); + CHECK_PROGRESS_ERROR_RET(pProgress, (Cloud::tr("Failed to list images")), RTEXITCODE_FAILURE); + + CHECK_ERROR2_RET(hrc, + pVMNamesHolder, COMGETTER(Values)(ComSafeArrayAsOutParam(arrayVMNames)), + RTEXITCODE_FAILURE); + CHECK_ERROR2_RET(hrc, + pVMIdsHolder, COMGETTER(Values)(ComSafeArrayAsOutParam(arrayVMIds)), + RTEXITCODE_FAILURE); + + RTPrintf(Cloud::tr("The list of the images for the cloud profile \'%ls\'\nand compartment \'%s\':\n"), + bstrProfileName.raw(), strCompartmentId.c_str()); + size_t cNames = arrayVMNames.size(); + size_t cIds = arrayVMIds.size(); + for (size_t k = 0; k < cNames; k++) + { + Bstr value; + if (k < cIds) + value = arrayVMIds[k]; + RTPrintf("\t%ls = %ls\n", arrayVMNames[k], value.raw()); + } + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + + +/** + * List all available cloud vnic attachments for the specified cloud provider. + * + * @returns RTEXITCODE + * @param a is the list of passed arguments + * @param iFirst is the position of the first unparsed argument in the arguments list + * @param pCommonOpts is a pointer to the structure CLOUDCOMMONOPT with some common + * arguments which have been already parsed before + */ +static RTEXITCODE listCloudVnicAttachments(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + static const RTGETOPTDEF s_aOptions[] = + { + { "--compartment-id", 'c', RTGETOPT_REQ_STRING }, + { "--filter", 'f', RTGETOPT_REQ_STRING },/*instanceId=, vnicId=, domainName=*/ + { "help", 'h', RTGETOPT_REQ_NOTHING }, + { "--help", 'h', RTGETOPT_REQ_NOTHING } + }; + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + com::SafeArray parameters; + Utf8Str strCompartmentId; + Utf8Str filterList; + HRESULT hrc = S_OK; + + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'c': + strCompartmentId = ValueUnion.psz; + Bstr(Utf8Str("compartmentId=").append(ValueUnion.psz)).detachTo(parameters.appendedRaw()); + break; + + case 'f': + filterList.append(ValueUnion.psz).append(","); + Bstr(Utf8Str(ValueUnion.psz)).detachTo(parameters.appendedRaw()); + break; + case 'h': + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + RTPrintf(Cloud::tr("Filters: \'%s\' \n"), filterList.c_str()); + + /* Delayed check. It allows us to print help information.*/ + hrc = checkAndSetCommonOptions(a, pCommonOpts); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + ComPtr pVirtualBox = a->virtualBox; + + ComPtr pCloudProviderManager; + CHECK_ERROR2_RET(hrc, pVirtualBox, + COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()), + RTEXITCODE_FAILURE); + + ComPtr pCloudProvider; + CHECK_ERROR2_RET(hrc, pCloudProviderManager, + GetProviderByShortName(Bstr(pCommonOpts->provider.pszProviderName).raw(), pCloudProvider.asOutParam()), + RTEXITCODE_FAILURE); + + ComPtr pCloudProfile; + CHECK_ERROR2_RET(hrc, pCloudProvider, + GetProfileByName(Bstr(pCommonOpts->profile.pszProfileName).raw(), pCloudProfile.asOutParam()), + RTEXITCODE_FAILURE); + + if (strCompartmentId.isNotEmpty()) + { + CHECK_ERROR2_RET(hrc, pCloudProfile, + SetProperty(Bstr("compartment").raw(), Bstr(strCompartmentId).raw()),\ + RTEXITCODE_FAILURE); + } + else + { + RTPrintf(Cloud::tr("Parameter \'compartment\' is empty or absent.\n" + "Trying to get the compartment from the passed cloud profile \'%s\'\n"), pCommonOpts->profile.pszProfileName); + Bstr bStrCompartmentId; + CHECK_ERROR2_RET(hrc, pCloudProfile, + GetProperty(Bstr("compartment").raw(), bStrCompartmentId.asOutParam()), + RTEXITCODE_FAILURE); + strCompartmentId = bStrCompartmentId; + if (strCompartmentId.isNotEmpty()) + RTPrintf(Cloud::tr("Found the compartment \'%s\':\n"), strCompartmentId.c_str()); + else + return errorArgument(Cloud::tr("Parameter --compartment-id is required.")); + } + + Bstr bstrProfileName; + pCloudProfile->COMGETTER(Name)(bstrProfileName.asOutParam()); + + ComObjPtr oCloudClient; + CHECK_ERROR2_RET(hrc, pCloudProfile, + CreateCloudClient(oCloudClient.asOutParam()), + RTEXITCODE_FAILURE); + + ComPtr pVnicAttachmentIdsHolder; + ComPtr pVnicIdsHolder; + com::SafeArray arrayVnicAttachmentIds; + com::SafeArray arrayVnicIds; + ComPtr pProgress; + + RTPrintf(Cloud::tr("Reply is in the form \'Vnic attachment \': \n\t \'Vnic \'\n")); + CHECK_ERROR2_RET(hrc, oCloudClient, + ListVnicAttachments(ComSafeArrayAsInParam(parameters), + pVnicAttachmentIdsHolder.asOutParam(), + pVnicIdsHolder.asOutParam(), + pProgress.asOutParam()), + RTEXITCODE_FAILURE); + showProgress(pProgress); + CHECK_PROGRESS_ERROR_RET(pProgress, (Cloud::tr("Failed to list Vnic attachments")), RTEXITCODE_FAILURE); + + CHECK_ERROR2_RET(hrc, + pVnicAttachmentIdsHolder, COMGETTER(Values)(ComSafeArrayAsOutParam(arrayVnicAttachmentIds)), + RTEXITCODE_FAILURE); + CHECK_ERROR2_RET(hrc, + pVnicIdsHolder, COMGETTER(Values)(ComSafeArrayAsOutParam(arrayVnicIds)), + RTEXITCODE_FAILURE); + + RTPrintf(Cloud::tr("The list of the Vnic attachments:\n")); + size_t cVnicAttchIds = arrayVnicAttachmentIds.size(); + size_t cVnicIds = arrayVnicIds.size(); + + if (cVnicAttchIds == 0) + RTPrintf(Cloud::tr("\tEmpty\n")); + else + { + Bstr value; + for (size_t k = 0; k < cVnicAttchIds; k++) + { + if (k < cVnicIds) + value = arrayVnicIds[k]; + RTPrintf(Cloud::tr("Vnic attachment id [%ls]:\n\t Vnic id - %ls\n"), arrayVnicAttachmentIds[k], value.raw()); + } + } + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + + +/** + * General function which handles the "list" commands + * + * @returns RTEXITCODE + * @param a is the list of passed arguments + * @param iFirst is the position of the first unparsed argument in the arguments list + * @param pCommonOpts is a pointer to the structure CLOUDCOMMONOPT with some common + * arguments which have been already parsed before + */ +static RTEXITCODE handleCloudLists(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + enum + { + kCloudListIota = 1000, + kCloudList_Images, + kCloudList_Instances, + kCloudList_Machines, + kCloudList_Networks, + kCloudList_Objects, + kCloudList_Subnets, + kCloudList_Vcns, + kCloudList_VnicAttachments, + }; + + static const RTGETOPTDEF s_aOptions[] = + { + { "images", kCloudList_Images, RTGETOPT_REQ_NOTHING }, + { "instances", kCloudList_Instances, RTGETOPT_REQ_NOTHING }, + { "machines", kCloudList_Machines, RTGETOPT_REQ_NOTHING }, + { "networks", kCloudList_Networks, RTGETOPT_REQ_NOTHING }, + { "objects", kCloudList_Objects, RTGETOPT_REQ_NOTHING }, + { "subnets", kCloudList_Subnets, RTGETOPT_REQ_NOTHING }, + { "vcns", kCloudList_Vcns, RTGETOPT_REQ_NOTHING }, + { "vms", kCloudList_Machines, RTGETOPT_REQ_NOTHING }, + { "vnicattachments", kCloudList_VnicAttachments, RTGETOPT_REQ_NOTHING }, + + { "help", 'h', RTGETOPT_REQ_NOTHING }, + { "-?", 'h', RTGETOPT_REQ_NOTHING }, + { "-help", 'h', RTGETOPT_REQ_NOTHING }, + { "--help", 'h', RTGETOPT_REQ_NOTHING }, + }; + + if (a->argc == iFirst) + { + RTPrintf(Cloud::tr("Empty command parameter list, show help.\n")); + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + } + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + int c; + RTGETOPTUNION ValueUnion; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case kCloudList_Images: + setCurrentSubcommand(HELP_SCOPE_CLOUDLIST_IMAGES); + return listCloudImages(a, GetState.iNext, pCommonOpts); + + case kCloudList_Instances: + setCurrentSubcommand(HELP_SCOPE_CLOUDLIST_INSTANCES); + return listCloudInstances(a, GetState.iNext, pCommonOpts); + case kCloudList_Machines: + return listCloudMachines(a, GetState.iNext, + pCommonOpts->provider.pszProviderName, + pCommonOpts->profile.pszProfileName); + + case kCloudList_VnicAttachments: + setCurrentSubcommand(HELP_SCOPE_CLOUDLIST_VNICATTACHMENTS); + return listCloudVnicAttachments(a, GetState.iNext, pCommonOpts); + + case 'h': + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + return errorNoSubcommand(); +} + + +static RTEXITCODE createCloudInstance(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + HRESULT hrc = S_OK; + + enum + { + kInstanceIota = 1000, + kInstance_ShapeCpu, + kInstance_ShapeMemory, + }; + + static const RTGETOPTDEF s_aOptions[] = + { + { "--image-id", 'i', RTGETOPT_REQ_STRING }, + { "--boot-volume-id", 'v', RTGETOPT_REQ_STRING }, + { "--display-name", 'n', RTGETOPT_REQ_STRING }, + { "--launch-mode", 'm', RTGETOPT_REQ_STRING }, + { "--shape", 's', RTGETOPT_REQ_STRING }, + { "--shape-cpus", kInstance_ShapeCpu, RTGETOPT_REQ_UINT32 }, + { "--shape-memory", kInstance_ShapeMemory, RTGETOPT_REQ_UINT32 }, + { "--domain-name", 'd', RTGETOPT_REQ_STRING }, + { "--boot-disk-size", 'b', RTGETOPT_REQ_STRING }, + { "--publicip", 'p', RTGETOPT_REQ_STRING }, + { "--subnet", 't', RTGETOPT_REQ_STRING }, + { "--privateip", 'P', RTGETOPT_REQ_STRING }, + { "--launch", 'l', RTGETOPT_REQ_STRING }, + { "--public-ssh-key", 'k', RTGETOPT_REQ_STRING }, + { "--cloud-init-script-path", 'c', RTGETOPT_REQ_STRING }, + { "help", 'h', RTGETOPT_REQ_NOTHING }, + { "--help", 'h', RTGETOPT_REQ_NOTHING } + }; + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + if (a->argc == iFirst) + { + RTPrintf(Cloud::tr("Empty command parameter list, show help.\n")); + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + } + + ComPtr pAppliance; + CHECK_ERROR2_RET(hrc, a->virtualBox, CreateAppliance(pAppliance.asOutParam()), RTEXITCODE_FAILURE); + ULONG vsdNum = 1; + CHECK_ERROR2_RET(hrc, pAppliance, CreateVirtualSystemDescriptions(1, &vsdNum), RTEXITCODE_FAILURE); + com::SafeIfaceArray virtualSystemDescriptions; + CHECK_ERROR2_RET(hrc, pAppliance, + COMGETTER(VirtualSystemDescriptions)(ComSafeArrayAsOutParam(virtualSystemDescriptions)), + RTEXITCODE_FAILURE); + ComPtr pVSD = virtualSystemDescriptions[0]; + + Utf8Str strDisplayName, strImageId, strBootVolumeId, strPublicSSHKey; + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'i': + strImageId = ValueUnion.psz; + pVSD->AddDescription(VirtualSystemDescriptionType_CloudImageId, + Bstr(ValueUnion.psz).raw(), NULL); + break; + + case 'v': + strBootVolumeId = ValueUnion.psz; + pVSD->AddDescription(VirtualSystemDescriptionType_CloudBootVolumeId, + Bstr(ValueUnion.psz).raw(), NULL); + break; + case 'n': + strDisplayName = ValueUnion.psz; + pVSD->AddDescription(VirtualSystemDescriptionType_Name, + Bstr(ValueUnion.psz).raw(), NULL); + break; + case 'm': + pVSD->AddDescription(VirtualSystemDescriptionType_CloudOCILaunchMode, + Bstr(ValueUnion.psz).raw(), NULL); + break; + + case 's': + pVSD->AddDescription(VirtualSystemDescriptionType_CloudInstanceShape, + Bstr(ValueUnion.psz).raw(), NULL); + break; + + case kInstance_ShapeCpu: + pVSD->AddDescription(VirtualSystemDescriptionType_CloudShapeCpus, + BstrFmt("%RI32", ValueUnion.u32).raw(), NULL); + break; + + case kInstance_ShapeMemory: + pVSD->AddDescription(VirtualSystemDescriptionType_CloudShapeMemory, + BstrFmt("%RI32", ValueUnion.u32).raw(), NULL); + break; + + case 'd': + pVSD->AddDescription(VirtualSystemDescriptionType_CloudDomain, + Bstr(ValueUnion.psz).raw(), NULL); + break; + case 'b': + pVSD->AddDescription(VirtualSystemDescriptionType_CloudBootDiskSize, + Bstr(ValueUnion.psz).raw(), NULL); + break; + case 'p': + pVSD->AddDescription(VirtualSystemDescriptionType_CloudPublicIP, + Bstr(ValueUnion.psz).raw(), NULL); + break; + case 'P': + pVSD->AddDescription(VirtualSystemDescriptionType_CloudPrivateIP, + Bstr(ValueUnion.psz).raw(), NULL); + break; + case 't': + pVSD->AddDescription(VirtualSystemDescriptionType_CloudOCISubnet, + Bstr(ValueUnion.psz).raw(), NULL); + break; + case 'l': + { + Utf8Str strLaunch(ValueUnion.psz); + if (strLaunch.isNotEmpty() && (strLaunch.equalsIgnoreCase("true") || strLaunch.equalsIgnoreCase("false"))) + pVSD->AddDescription(VirtualSystemDescriptionType_CloudLaunchInstance, + Bstr(ValueUnion.psz).raw(), NULL); + break; + } + case 'k': + strPublicSSHKey = ValueUnion.psz; + pVSD->AddDescription(VirtualSystemDescriptionType_CloudPublicSSHKey, + Bstr(ValueUnion.psz).raw(), NULL); + break; + case 'c': + pVSD->AddDescription(VirtualSystemDescriptionType_CloudInitScriptPath, + Bstr(ValueUnion.psz).raw(), NULL); + break; + case 'h': + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + default: + return errorGetOpt(c, &ValueUnion); + } + } + + /* Delayed check. It allows us to print help information.*/ + hrc = checkAndSetCommonOptions(a, pCommonOpts); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + if (strPublicSSHKey.isEmpty()) + RTPrintf(Cloud::tr("Warning!!! Public SSH key doesn't present in the passed arguments...\n")); + + if (strImageId.isNotEmpty() && strBootVolumeId.isNotEmpty()) + return errorArgument(Cloud::tr("Parameters --image-id and --boot-volume-id are mutually exclusive. " + "Only one of them must be presented.")); + + if (strImageId.isEmpty() && strBootVolumeId.isEmpty()) + return errorArgument(Cloud::tr("Missing parameter --image-id or --boot-volume-id. One of them must be presented.")); + + ComPtr pCloudProfile = pCommonOpts->profile.pCloudProfile; + + pVSD->AddDescription(VirtualSystemDescriptionType_CloudProfileName, + Bstr(pCommonOpts->profile.pszProfileName).raw(), + NULL); + + ComObjPtr oCloudClient; + CHECK_ERROR2_RET(hrc, pCloudProfile, + CreateCloudClient(oCloudClient.asOutParam()), + RTEXITCODE_FAILURE); + + ComPtr infoArray; + com::SafeArray pStrInfoArray; + ComPtr pProgress; + +#if 0 + /* + * OCI API returns an error during an instance creation if the image isn't available + * or in the inappropriate state. So the check can be omitted. + */ + RTPrintf(Cloud::tr("Checking the cloud image with id \'%s\'...\n"), strImageId.c_str()); + CHECK_ERROR2_RET(hrc, oCloudClient, + GetImageInfo(Bstr(strImageId).raw(), + infoArray.asOutParam(), + pProgress.asOutParam()), + RTEXITCODE_FAILURE); + + hrc = showProgress(pProgress); + CHECK_PROGRESS_ERROR_RET(pProgress, (Cloud::tr("Checking the cloud image failed")), RTEXITCODE_FAILURE); + + pProgress.setNull(); +#endif + + if (strImageId.isNotEmpty()) + RTPrintf(Cloud::tr("Creating cloud instance with name \'%s\' from the image \'%s\'...\n"), + strDisplayName.c_str(), strImageId.c_str()); + else + RTPrintf(Cloud::tr("Creating cloud instance with name \'%s\' from the boot volume \'%s\'...\n"), + strDisplayName.c_str(), strBootVolumeId.c_str()); + + CHECK_ERROR2_RET(hrc, oCloudClient, LaunchVM(pVSD, pProgress.asOutParam()), RTEXITCODE_FAILURE); + + hrc = showProgress(pProgress); + CHECK_PROGRESS_ERROR_RET(pProgress, (Cloud::tr("Creating cloud instance failed")), RTEXITCODE_FAILURE); + + if (SUCCEEDED(hrc)) + RTPrintf(Cloud::tr("Cloud instance was created successfully\n")); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +static RTEXITCODE updateCloudInstance(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + RT_NOREF(a); + RT_NOREF(iFirst); + RT_NOREF(pCommonOpts); + return RTEXITCODE_SUCCESS; +} + +static RTEXITCODE showCloudInstanceInfo(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + HRESULT hrc = S_OK; + + static const RTGETOPTDEF s_aOptions[] = + { + { "--id", 'i', RTGETOPT_REQ_STRING }, + { "help", 'h', RTGETOPT_REQ_NOTHING }, + { "--help", 'h', RTGETOPT_REQ_NOTHING } + }; + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + if (a->argc == iFirst) + { + RTPrintf(Cloud::tr("Empty command parameter list, show help.\n")); + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + } + + Utf8Str strInstanceId; + + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'i': + { + if (strInstanceId.isNotEmpty()) + return errorArgument(Cloud::tr("Duplicate parameter: --id")); + + strInstanceId = ValueUnion.psz; + if (strInstanceId.isEmpty()) + return errorArgument(Cloud::tr("Empty parameter: --id")); + + break; + } + case 'h': + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + /* Delayed check. It allows us to print help information.*/ + hrc = checkAndSetCommonOptions(a, pCommonOpts); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + if (strInstanceId.isEmpty()) + return errorArgument(Cloud::tr("Missing parameter: --id")); + + ComPtr pCloudProfile = pCommonOpts->profile.pCloudProfile; + + ComObjPtr oCloudClient; + CHECK_ERROR2_RET(hrc, pCloudProfile, + CreateCloudClient(oCloudClient.asOutParam()), + RTEXITCODE_FAILURE); + RTPrintf(Cloud::tr("Getting information about cloud instance with id %s...\n"), strInstanceId.c_str()); + RTPrintf(Cloud::tr("Reply is in the form \'setting name\' = \'value\'\n")); + + ComPtr pAppliance; + CHECK_ERROR2_RET(hrc, a->virtualBox, CreateAppliance(pAppliance.asOutParam()), RTEXITCODE_FAILURE); + + com::SafeIfaceArray vsdArray; + ULONG requestedVSDnums = 1; + ULONG newVSDnums = 0; + CHECK_ERROR2_RET(hrc, pAppliance, CreateVirtualSystemDescriptions(requestedVSDnums, &newVSDnums), RTEXITCODE_FAILURE); + if (requestedVSDnums != newVSDnums) + return RTEXITCODE_FAILURE; + + CHECK_ERROR2_RET(hrc, pAppliance, COMGETTER(VirtualSystemDescriptions)(ComSafeArrayAsOutParam(vsdArray)), RTEXITCODE_FAILURE); + ComPtr instanceDescription = vsdArray[0]; + + ComPtr progress; + CHECK_ERROR2_RET(hrc, oCloudClient, + GetInstanceInfo(Bstr(strInstanceId).raw(), instanceDescription, progress.asOutParam()), + RTEXITCODE_FAILURE); + + hrc = showProgress(progress); + CHECK_PROGRESS_ERROR_RET(progress, (Cloud::tr("Getting information about cloud instance failed")), RTEXITCODE_FAILURE); + + RTPrintf(Cloud::tr("Cloud instance info (provider '%s'):\n"), + pCommonOpts->provider.pszProviderName); + + struct vsdHReadable { + VirtualSystemDescriptionType_T vsdType; + Utf8Str strFound; + Utf8Str strNotFound; + }; + + const size_t vsdHReadableArraySize = 15;//the number of items in the vsdHReadableArray + vsdHReadable vsdHReadableArray[vsdHReadableArraySize] = { + {VirtualSystemDescriptionType_CloudDomain, Cloud::tr("Availability domain = %ls\n"), Cloud::tr("Availability domain wasn't found\n")}, + {VirtualSystemDescriptionType_Name, Cloud::tr("Instance displayed name = %ls\n"), Cloud::tr("Instance displayed name wasn't found\n")}, + {VirtualSystemDescriptionType_CloudInstanceState, Cloud::tr("Instance state = %ls\n"), Cloud::tr("Instance state wasn't found\n")}, + {VirtualSystemDescriptionType_CloudInstanceId, Cloud::tr("Instance Id = %ls\n"), Cloud::tr("Instance Id wasn't found\n")}, + {VirtualSystemDescriptionType_CloudInstanceDisplayName, Cloud::tr("Instance name = %ls\n"), Cloud::tr("Instance name wasn't found\n")}, + {VirtualSystemDescriptionType_CloudImageId, Cloud::tr("Bootable image Id = %ls\n"), + Cloud::tr("Image Id whom the instance is booted up wasn't found\n")}, + {VirtualSystemDescriptionType_CloudInstanceShape, Cloud::tr("Shape of the instance = %ls\n"), + Cloud::tr("The shape of the instance wasn't found\n")}, + {VirtualSystemDescriptionType_OS, Cloud::tr("Type of guest OS = %ls\n"), Cloud::tr("Type of guest OS wasn't found\n")}, + {VirtualSystemDescriptionType_Memory, Cloud::tr("RAM = %ls MB\n"), Cloud::tr("Value for RAM wasn't found\n")}, + {VirtualSystemDescriptionType_CPU, Cloud::tr("CPUs = %ls\n"), Cloud::tr("Numbers of CPUs weren't found\n")}, + {VirtualSystemDescriptionType_CloudPublicIP, Cloud::tr("Instance public IP = %ls\n"), Cloud::tr("Public IP wasn't found\n")}, + {VirtualSystemDescriptionType_Miscellaneous, "%ls\n", Cloud::tr("Miscellanious wasn't found\n")}, + {VirtualSystemDescriptionType_CloudInstanceFreeFormTags, "%ls\n", Cloud::tr("Free-form tags weren't found\n")}, + {VirtualSystemDescriptionType_CloudInstanceMetadata, "%ls\n", Cloud::tr("Metadata was't found\n")}, + {VirtualSystemDescriptionType_CloudInitScriptPath, "Cloud-init script: \n\t%ls\n", Cloud::tr("Cloud-init script wasn't found\n")} + }; + + com::SafeArray retTypes; + com::SafeArray aRefs; + com::SafeArray aOvfValues; + com::SafeArray aVBoxValues; + com::SafeArray aExtraConfigValues; + + for (size_t i=0; iGetDescriptionByType(vsdHReadableArray[i].vsdType, + ComSafeArrayAsOutParam(retTypes), + ComSafeArrayAsOutParam(aRefs), + ComSafeArrayAsOutParam(aOvfValues), + ComSafeArrayAsOutParam(aVBoxValues), + ComSafeArrayAsOutParam(aExtraConfigValues)); + if (FAILED(hrc) || aVBoxValues.size() == 0) + LogRel((vsdHReadableArray[i].strNotFound.c_str())); + else + { + LogRel(("Size is %d", aVBoxValues.size())); + for (size_t j = 0; jargc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + if (a->argc == iFirst) + { + RTPrintf(Cloud::tr("Empty command parameter list, show help.\n")); + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + } + + Utf8Str strInstanceId; + + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'i': + { + if (strInstanceId.isNotEmpty()) + return errorArgument(Cloud::tr("Duplicate parameter: --id")); + + strInstanceId = ValueUnion.psz; + if (strInstanceId.isEmpty()) + return errorArgument(Cloud::tr("Empty parameter: --id")); + + break; + } + case 'h': + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + /* Delayed check. It allows us to print help information.*/ + hrc = checkAndSetCommonOptions(a, pCommonOpts); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + if (strInstanceId.isEmpty()) + return errorArgument(Cloud::tr("Missing parameter: --id")); + + ComPtr pCloudProfile = pCommonOpts->profile.pCloudProfile; + + ComObjPtr oCloudClient; + CHECK_ERROR2_RET(hrc, pCloudProfile, + CreateCloudClient(oCloudClient.asOutParam()), + RTEXITCODE_FAILURE); + RTPrintf(Cloud::tr("Starting cloud instance with id %s...\n"), strInstanceId.c_str()); + + ComPtr progress; + CHECK_ERROR2_RET(hrc, oCloudClient, + StartInstance(Bstr(strInstanceId).raw(), progress.asOutParam()), + RTEXITCODE_FAILURE); + hrc = showProgress(progress); + CHECK_PROGRESS_ERROR_RET(progress, (Cloud::tr("Starting the cloud instance failed")), RTEXITCODE_FAILURE); + + if (SUCCEEDED(hrc)) + RTPrintf(Cloud::tr("Cloud instance with id %s (provider = '%s', profile = '%s') was started\n"), + strInstanceId.c_str(), + pCommonOpts->provider.pszProviderName, + pCommonOpts->profile.pszProfileName); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +static RTEXITCODE pauseCloudInstance(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + HRESULT hrc = S_OK; + + static const RTGETOPTDEF s_aOptions[] = + { + { "--id", 'i', RTGETOPT_REQ_STRING }, + { "help", 'h', RTGETOPT_REQ_NOTHING }, + { "--help", 'h', RTGETOPT_REQ_NOTHING } + }; + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + if (a->argc == iFirst) + { + RTPrintf(Cloud::tr("Empty command parameter list, show help.\n")); + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + } + + Utf8Str strInstanceId; + + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'i': + { + if (strInstanceId.isNotEmpty()) + return errorArgument(Cloud::tr("Duplicate parameter: --id")); + + strInstanceId = ValueUnion.psz; + if (strInstanceId.isEmpty()) + return errorArgument(Cloud::tr("Empty parameter: --id")); + + break; + } + case 'h': + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + /* Delayed check. It allows us to print help information.*/ + hrc = checkAndSetCommonOptions(a, pCommonOpts); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + if (strInstanceId.isEmpty()) + return errorArgument(Cloud::tr("Missing parameter: --id")); + + ComPtr pCloudProfile = pCommonOpts->profile.pCloudProfile; + + ComObjPtr oCloudClient; + CHECK_ERROR2_RET(hrc, pCloudProfile, + CreateCloudClient(oCloudClient.asOutParam()), + RTEXITCODE_FAILURE); + RTPrintf(Cloud::tr("Pausing cloud instance with id %s...\n"), strInstanceId.c_str()); + + ComPtr progress; + CHECK_ERROR2_RET(hrc, oCloudClient, + PauseInstance(Bstr(strInstanceId).raw(), progress.asOutParam()), + RTEXITCODE_FAILURE); + hrc = showProgress(progress); + CHECK_PROGRESS_ERROR_RET(progress, (Cloud::tr("Pause the cloud instance failed")), RTEXITCODE_FAILURE); + + if (SUCCEEDED(hrc)) + RTPrintf(Cloud::tr("Cloud instance with id %s (provider = '%s', profile = '%s') was paused\n"), + strInstanceId.c_str(), + pCommonOpts->provider.pszProviderName, + pCommonOpts->profile.pszProfileName); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +static RTEXITCODE terminateCloudInstance(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + HRESULT hrc = S_OK; + + static const RTGETOPTDEF s_aOptions[] = + { + { "--id", 'i', RTGETOPT_REQ_STRING }, + { "help", 'h', RTGETOPT_REQ_NOTHING }, + { "--help", 'h', RTGETOPT_REQ_NOTHING } + }; + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + if (a->argc == iFirst) + { + RTPrintf(Cloud::tr("Empty command parameter list, show help.\n")); + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + } + + Utf8Str strInstanceId; + + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'i': + { + if (strInstanceId.isNotEmpty()) + return errorArgument(Cloud::tr("Duplicate parameter: --id")); + + strInstanceId = ValueUnion.psz; + if (strInstanceId.isEmpty()) + return errorArgument(Cloud::tr("Empty parameter: --id")); + + break; + } + case 'h': + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + /* Delayed check. It allows us to print help information.*/ + hrc = checkAndSetCommonOptions(a, pCommonOpts); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + if (strInstanceId.isEmpty()) + return errorArgument(Cloud::tr("Missing parameter: --id")); + + + ComPtr pCloudProfile = pCommonOpts->profile.pCloudProfile; + + ComObjPtr oCloudClient; + CHECK_ERROR2_RET(hrc, pCloudProfile, + CreateCloudClient(oCloudClient.asOutParam()), + RTEXITCODE_FAILURE); + RTPrintf(Cloud::tr("Terminating cloud instance with id %s...\n"), strInstanceId.c_str()); + + ComPtr progress; + CHECK_ERROR2_RET(hrc, oCloudClient, + TerminateInstance(Bstr(strInstanceId).raw(), progress.asOutParam()), + RTEXITCODE_FAILURE); + hrc = showProgress(progress); + CHECK_PROGRESS_ERROR_RET(progress, (Cloud::tr("Termination the cloud instance failed")), RTEXITCODE_FAILURE); + + if (SUCCEEDED(hrc)) + RTPrintf(Cloud::tr("Cloud instance with id %s (provider = '%s', profile = '%s') was terminated\n"), + strInstanceId.c_str(), + pCommonOpts->provider.pszProviderName, + pCommonOpts->profile.pszProfileName); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +static RTEXITCODE resetCloudInstance(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + HRESULT hrc = S_OK; + + static const RTGETOPTDEF s_aOptions[] = + { + { "--id", 'i', RTGETOPT_REQ_STRING }, + { "help", 'h', RTGETOPT_REQ_NOTHING }, + { "--help", 'h', RTGETOPT_REQ_NOTHING } + }; + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + if (a->argc == iFirst) + { + RTPrintf(Cloud::tr("Empty command parameter list, show help.\n")); + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + } + + Utf8Str strInstanceId; + + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'i': + { + if (strInstanceId.isNotEmpty()) + return errorArgument(Cloud::tr("Duplicate parameter: --id")); + + strInstanceId = ValueUnion.psz; + if (strInstanceId.isEmpty()) + return errorArgument(Cloud::tr("Empty parameter: --id")); + + break; + } + case 'h': + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + /* Delayed check. It allows us to print help information.*/ + hrc = checkAndSetCommonOptions(a, pCommonOpts); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + if (strInstanceId.isEmpty()) + return errorArgument(Cloud::tr("Missing parameter: --id")); + + ComPtr pCloudProfile = pCommonOpts->profile.pCloudProfile; + + ComObjPtr oCloudClient; + CHECK_ERROR2_RET(hrc, pCloudProfile, + CreateCloudClient(oCloudClient.asOutParam()), + RTEXITCODE_FAILURE); + RTPrintf(Cloud::tr("Reset cloud instance with id %s...\n"), strInstanceId.c_str()); + + ComPtr progress; + CHECK_ERROR2_RET(hrc, oCloudClient, + ResetInstance(Bstr(strInstanceId).raw(), progress.asOutParam()), + RTEXITCODE_FAILURE); + hrc = showProgress(progress); + CHECK_PROGRESS_ERROR_RET(progress, (Cloud::tr("Reset the cloud instance failed")), RTEXITCODE_FAILURE); + + if (SUCCEEDED(hrc)) + RTPrintf(Cloud::tr("Cloud instance with id %s (provider = '%s', profile = '%s') was reset\n"), + strInstanceId.c_str(), + pCommonOpts->provider.pszProviderName, + pCommonOpts->profile.pszProfileName); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +static RTEXITCODE handleCloudInstance(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + enum + { + kCloudInstanceIota = 1000, + kCloudInstance_Create, + kCloudInstance_Info, + kCloudInstance_Pause, + kCloudInstance_Start, + kCloudInstance_Terminate, + kCloudInstance_Update, + kCloudInstance_Reset, + }; + + static const RTGETOPTDEF s_aOptions[] = + { + { "create", kCloudInstance_Create, RTGETOPT_REQ_NOTHING }, + { "info", kCloudInstance_Info, RTGETOPT_REQ_NOTHING }, + { "pause", kCloudInstance_Pause, RTGETOPT_REQ_NOTHING }, + { "start", kCloudInstance_Start, RTGETOPT_REQ_NOTHING }, + { "terminate", kCloudInstance_Terminate, RTGETOPT_REQ_NOTHING }, + { "update", kCloudInstance_Update, RTGETOPT_REQ_NOTHING }, + { "reset", kCloudInstance_Reset, RTGETOPT_REQ_NOTHING }, + + { "help", 'h', RTGETOPT_REQ_NOTHING }, + { "-?", 'h', RTGETOPT_REQ_NOTHING }, + { "-help", 'h', RTGETOPT_REQ_NOTHING }, + { "--help", 'h', RTGETOPT_REQ_NOTHING }, + }; + + if (a->argc == iFirst) + { + RTPrintf(Cloud::tr("Empty command parameter list, show help.\n")); + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + } + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + int c; + RTGETOPTUNION ValueUnion; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + /* Sub-commands: */ + case kCloudInstance_Create: + setCurrentSubcommand(HELP_SCOPE_CLOUDINSTANCE_CREATE); + return createCloudInstance(a, GetState.iNext, pCommonOpts); + + case kCloudInstance_Start: + setCurrentSubcommand(HELP_SCOPE_CLOUDINSTANCE_START); + return startCloudInstance(a, GetState.iNext, pCommonOpts); + + case kCloudInstance_Pause: + setCurrentSubcommand(HELP_SCOPE_CLOUDINSTANCE_PAUSE); + return pauseCloudInstance(a, GetState.iNext, pCommonOpts); + + case kCloudInstance_Info: + setCurrentSubcommand(HELP_SCOPE_CLOUDINSTANCE_INFO); + return showCloudInstanceInfo(a, GetState.iNext, pCommonOpts); + + case kCloudInstance_Update: +// setCurrentSubcommand(HELP_SCOPE_CLOUDINSTANCE_UPDATE); + return updateCloudInstance(a, GetState.iNext, pCommonOpts); + + case kCloudInstance_Terminate: + setCurrentSubcommand(HELP_SCOPE_CLOUDINSTANCE_TERMINATE); + return terminateCloudInstance(a, GetState.iNext, pCommonOpts); + + case kCloudInstance_Reset: + setCurrentSubcommand(HELP_SCOPE_CLOUDINSTANCE_RESET); + return resetCloudInstance(a, GetState.iNext, pCommonOpts); + + case 'h': + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + return errorNoSubcommand(); +} + + +static RTEXITCODE createCloudImage(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + HRESULT hrc = S_OK; + + static const RTGETOPTDEF s_aOptions[] = + { + { "--object-name", 'o', RTGETOPT_REQ_STRING }, + { "--bucket-name", 'b', RTGETOPT_REQ_STRING }, + { "--compartment-id", 'c', RTGETOPT_REQ_STRING }, + { "--instance-id", 'i', RTGETOPT_REQ_STRING }, + { "--display-name", 'd', RTGETOPT_REQ_STRING }, + { "--launch-mode", 'm', RTGETOPT_REQ_STRING }, + { "help", 'h', RTGETOPT_REQ_NOTHING }, + { "--help", 'h', RTGETOPT_REQ_NOTHING } + }; + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + if (a->argc == iFirst) + { + RTPrintf(Cloud::tr("Empty command parameter list, show help.\n")); + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + } + + Utf8Str strCompartmentId; + Utf8Str strInstanceId; + Utf8Str strDisplayName; + Utf8Str strBucketName; + Utf8Str strObjectName; + com::SafeArray parameters; + + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'c': + strCompartmentId=ValueUnion.psz; + Bstr(Utf8Str("compartment-id=").append(ValueUnion.psz)).detachTo(parameters.appendedRaw()); + break; + case 'i': + strInstanceId=ValueUnion.psz; + Bstr(Utf8Str("instance-id=").append(ValueUnion.psz)).detachTo(parameters.appendedRaw()); + break; + case 'd': + strDisplayName=ValueUnion.psz; + Bstr(Utf8Str("display-name=").append(ValueUnion.psz)).detachTo(parameters.appendedRaw()); + break; + case 'o': + strObjectName=ValueUnion.psz; + Bstr(Utf8Str("object-name=").append(ValueUnion.psz)).detachTo(parameters.appendedRaw()); + break; + case 'b': + strBucketName=ValueUnion.psz; + Bstr(Utf8Str("bucket-name=").append(ValueUnion.psz)).detachTo(parameters.appendedRaw()); + break; + case 'm': + strBucketName=ValueUnion.psz; + Bstr(Utf8Str("launch-mode=").append(ValueUnion.psz)).detachTo(parameters.appendedRaw()); + break; + case 'h': + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + default: + return errorGetOpt(c, &ValueUnion); + } + } + + /* Delayed check. It allows us to print help information.*/ + hrc = checkAndSetCommonOptions(a, pCommonOpts); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + if (strInstanceId.isNotEmpty() && strObjectName.isNotEmpty()) + return errorArgument(Cloud::tr("Conflicting parameters: --instance-id and --object-name can't be used together. Choose one.")); + + ComPtr pCloudProfile = pCommonOpts->profile.pCloudProfile; + + ComObjPtr oCloudClient; + CHECK_ERROR2_RET(hrc, pCloudProfile, + CreateCloudClient(oCloudClient.asOutParam()), + RTEXITCODE_FAILURE); + if (strInstanceId.isNotEmpty()) + RTPrintf(Cloud::tr("Creating cloud image with name \'%s\' from the instance \'%s\'...\n"), + strDisplayName.c_str(), strInstanceId.c_str()); + else + RTPrintf(Cloud::tr("Creating cloud image with name \'%s\' from the object \'%s\' in the bucket \'%s\'...\n"), + strDisplayName.c_str(), strObjectName.c_str(), strBucketName.c_str()); + + ComPtr progress; + CHECK_ERROR2_RET(hrc, oCloudClient, + CreateImage(ComSafeArrayAsInParam(parameters), progress.asOutParam()), + RTEXITCODE_FAILURE); + hrc = showProgress(progress); + CHECK_PROGRESS_ERROR_RET(progress, (Cloud::tr("Creating cloud image failed")), RTEXITCODE_FAILURE); + + if (SUCCEEDED(hrc)) + RTPrintf(Cloud::tr("Cloud image was created successfully\n")); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + + +static RTEXITCODE exportCloudImage(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + HRESULT hrc = S_OK; + + static const RTGETOPTDEF s_aOptions[] = + { + { "--id", 'i', RTGETOPT_REQ_STRING }, + { "--bucket-name", 'b', RTGETOPT_REQ_STRING }, + { "--object-name", 'o', RTGETOPT_REQ_STRING }, + { "--display-name", 'd', RTGETOPT_REQ_STRING }, + { "--launch-mode", 'm', RTGETOPT_REQ_STRING }, + { "help", 'h', RTGETOPT_REQ_NOTHING }, + { "--help", 'h', RTGETOPT_REQ_NOTHING } + }; + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + if (a->argc == iFirst) + { + RTPrintf(Cloud::tr("Empty command parameter list, show help.\n")); + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + } + + Utf8Str strImageId; /* XXX: this is vbox "image", i.e. medium */ + Utf8Str strBucketName; + Utf8Str strObjectName; + Utf8Str strDisplayName; + Utf8Str strLaunchMode; + com::SafeArray parameters; + + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'b': /* --bucket-name */ + { + if (strBucketName.isNotEmpty()) + return errorArgument(Cloud::tr("Duplicate parameter: --bucket-name")); + + strBucketName = ValueUnion.psz; + if (strBucketName.isEmpty()) + return errorArgument(Cloud::tr("Empty parameter: --bucket-name")); + + break; + } + + case 'o': /* --object-name */ + { + if (strObjectName.isNotEmpty()) + return errorArgument(Cloud::tr("Duplicate parameter: --object-name")); + + strObjectName = ValueUnion.psz; + if (strObjectName.isEmpty()) + return errorArgument(Cloud::tr("Empty parameter: --object-name")); + + break; + } + + case 'i': /* --id */ + { + if (strImageId.isNotEmpty()) + return errorArgument(Cloud::tr("Duplicate parameter: --id")); + + strImageId = ValueUnion.psz; + if (strImageId.isEmpty()) + return errorArgument(Cloud::tr("Empty parameter: --id")); + + break; + } + + case 'd': /* --display-name */ + { + if (strDisplayName.isNotEmpty()) + return errorArgument(Cloud::tr("Duplicate parameter: --display-name")); + + strDisplayName = ValueUnion.psz; + if (strDisplayName.isEmpty()) + return errorArgument(Cloud::tr("Empty parameter: --display-name")); + + break; + } + + case 'm': /* --launch-mode */ + { + if (strLaunchMode.isNotEmpty()) + return errorArgument(Cloud::tr("Duplicate parameter: --launch-mode")); + + strLaunchMode = ValueUnion.psz; + if (strLaunchMode.isEmpty()) + return errorArgument(Cloud::tr("Empty parameter: --launch-mode")); + + break; + } + + case 'h': + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + /* Delayed check. It allows us to print help information.*/ + hrc = checkAndSetCommonOptions(a, pCommonOpts); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + if (strImageId.isNotEmpty()) + BstrFmt("image-id=%s", strImageId.c_str()).detachTo(parameters.appendedRaw()); + else + return errorArgument(Cloud::tr("Missing parameter: --id")); + + if (strBucketName.isNotEmpty()) + BstrFmt("bucket-name=%s", strBucketName.c_str()).detachTo(parameters.appendedRaw()); + else + return errorArgument(Cloud::tr("Missing parameter: --bucket-name")); + + if (strObjectName.isNotEmpty()) + BstrFmt("object-name=%s", strObjectName.c_str()).detachTo(parameters.appendedRaw()); + + if (strDisplayName.isNotEmpty()) + BstrFmt("display-name=%s", strDisplayName.c_str()).detachTo(parameters.appendedRaw()); + + if (strLaunchMode.isNotEmpty()) + BstrFmt("launch-mode=%s", strLaunchMode.c_str()).detachTo(parameters.appendedRaw()); + + + ComPtr pCloudProfile = pCommonOpts->profile.pCloudProfile; + + ComObjPtr oCloudClient; + CHECK_ERROR2_RET(hrc, pCloudProfile, + CreateCloudClient(oCloudClient.asOutParam()), + RTEXITCODE_FAILURE); + + if (strObjectName.isNotEmpty()) + RTPrintf(Cloud::tr("Exporting image \'%s\' to the Cloud with name \'%s\'...\n"), + strImageId.c_str(), strObjectName.c_str()); + else + RTPrintf(Cloud::tr("Exporting image \'%s\' to the Cloud with default name\n"), + strImageId.c_str()); + + ComPtr pVirtualBox = a->virtualBox; + SafeIfaceArray aImageList; + CHECK_ERROR2_RET(hrc, pVirtualBox, + COMGETTER(HardDisks)(ComSafeArrayAsOutParam(aImageList)), + RTEXITCODE_FAILURE); + + ComPtr pImage; + size_t cImages = aImageList.size(); + bool fFound = false; + for (size_t i = 0; i < cImages; ++i) + { + pImage = aImageList[i]; + Bstr bstrImageId; + hrc = pImage->COMGETTER(Id)(bstrImageId.asOutParam()); + if (FAILED(hrc)) + continue; + + com::Guid imageId(bstrImageId); + + if (!imageId.isValid() || imageId.isZero()) + continue; + + if (!strImageId.compare(imageId.toString())) + { + fFound = true; + RTPrintf(Cloud::tr("Image %s was found\n"), strImageId.c_str()); + break; + } + } + + if (!fFound) + { + RTPrintf(Cloud::tr("Process of exporting the image to the Cloud was interrupted. The image wasn't found.\n")); + return RTEXITCODE_FAILURE; + } + + ComPtr progress; + CHECK_ERROR2_RET(hrc, oCloudClient, + ExportImage(pImage, ComSafeArrayAsInParam(parameters), progress.asOutParam()), + RTEXITCODE_FAILURE); + hrc = showProgress(progress); + CHECK_PROGRESS_ERROR_RET(progress, (Cloud::tr("Export the image to the Cloud failed")), RTEXITCODE_FAILURE); + + if (SUCCEEDED(hrc)) + RTPrintf(Cloud::tr("Export the image to the Cloud was successfull\n")); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +static RTEXITCODE importCloudImage(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + HRESULT hrc = S_OK; + + static const RTGETOPTDEF s_aOptions[] = + { + { "--id", 'i', RTGETOPT_REQ_STRING }, + { "--bucket-name", 'b', RTGETOPT_REQ_STRING }, + { "--object-name", 'o', RTGETOPT_REQ_STRING }, + { "help", 'h', RTGETOPT_REQ_NOTHING }, + { "--help", 'h', RTGETOPT_REQ_NOTHING } + }; + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + if (a->argc == iFirst) + { + RTPrintf(Cloud::tr("Empty command parameter list, show help.\n")); + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + } + + Utf8Str strImageId; + Utf8Str strCompartmentId; + Utf8Str strBucketName; + Utf8Str strObjectName; + Utf8Str strDisplayName; + com::SafeArray parameters; + + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'i': + strImageId=ValueUnion.psz; + break; + case 'b': + strBucketName=ValueUnion.psz; + Bstr(Utf8Str("bucket-name=").append(ValueUnion.psz)).detachTo(parameters.appendedRaw()); + break; + case 'o': + strObjectName=ValueUnion.psz; + Bstr(Utf8Str("object-name=").append(ValueUnion.psz)).detachTo(parameters.appendedRaw()); + break; + case 'h': + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + default: + return errorGetOpt(c, &ValueUnion); + } + } + + /* Delayed check. It allows us to print help information.*/ + hrc = checkAndSetCommonOptions(a, pCommonOpts); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + ComPtr pCloudProfile = pCommonOpts->profile.pCloudProfile; + + ComPtr pVirtualBox = a->virtualBox; + ComObjPtr oCloudClient; + CHECK_ERROR2_RET(hrc, pCloudProfile, + CreateCloudClient(oCloudClient.asOutParam()), + RTEXITCODE_FAILURE); + RTPrintf(Cloud::tr("Creating an object \'%s\' from the cloud image \'%s\'...\n"), strObjectName.c_str(), strImageId.c_str()); + + ComPtr progress; + CHECK_ERROR2_RET(hrc, oCloudClient, + ImportImage(Bstr(strImageId).raw(), ComSafeArrayAsInParam(parameters), progress.asOutParam()), + RTEXITCODE_FAILURE); + hrc = showProgress(progress); + CHECK_PROGRESS_ERROR_RET(progress, (Cloud::tr("Cloud image import failed")), RTEXITCODE_FAILURE); + + if (SUCCEEDED(hrc)) + { + RTPrintf(Cloud::tr("Cloud image was imported successfully. Find the downloaded object with the name %s " + "in the system temp folder (find the possible environment variables like TEMP, TMP and etc.)\n"), + strObjectName.c_str()); + } + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +static RTEXITCODE showCloudImageInfo(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + HRESULT hrc = S_OK; + + static const RTGETOPTDEF s_aOptions[] = + { + { "--id", 'i', RTGETOPT_REQ_STRING }, + { "help", 'h', RTGETOPT_REQ_NOTHING }, + { "--help", 'h', RTGETOPT_REQ_NOTHING } + }; + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + if (a->argc == iFirst) + { + RTPrintf(Cloud::tr("Empty command parameter list, show help.\n")); + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + } + + Utf8Str strImageId; + + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'i': + strImageId = ValueUnion.psz; + break; + case 'h': + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + default: + return errorGetOpt(c, &ValueUnion); + } + } + + /* Delayed check. It allows us to print help information.*/ + hrc = checkAndSetCommonOptions(a, pCommonOpts); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + ComPtr pCloudProfile = pCommonOpts->profile.pCloudProfile; + + ComObjPtr oCloudClient; + CHECK_ERROR2_RET(hrc, pCloudProfile, + CreateCloudClient(oCloudClient.asOutParam()), + RTEXITCODE_FAILURE); + RTPrintf(Cloud::tr("Getting information about the cloud image with id \'%s\'...\n"), strImageId.c_str()); + + ComPtr infoArray; + com::SafeArray pStrInfoArray; + ComPtr pProgress; + + RTPrintf(Cloud::tr("Reply is in the form \'image property\' = \'value\'\n")); + CHECK_ERROR2_RET(hrc, oCloudClient, + GetImageInfo(Bstr(strImageId).raw(), + infoArray.asOutParam(), + pProgress.asOutParam()), + RTEXITCODE_FAILURE); + + hrc = showProgress(pProgress); + CHECK_PROGRESS_ERROR_RET(pProgress, (Cloud::tr("Getting information about the cloud image failed")), RTEXITCODE_FAILURE); + + CHECK_ERROR2_RET(hrc, + infoArray, COMGETTER(Values)(ComSafeArrayAsOutParam(pStrInfoArray)), + RTEXITCODE_FAILURE); + + RTPrintf(Cloud::tr("General information about the image:\n")); + size_t cParamNames = pStrInfoArray.size(); + for (size_t k = 0; k < cParamNames; k++) + { + Utf8Str data(pStrInfoArray[k]); + RTPrintf("\t%s\n", data.c_str()); + } + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +static RTEXITCODE updateCloudImage(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + RT_NOREF(a); + RT_NOREF(iFirst); + RT_NOREF(pCommonOpts); + return RTEXITCODE_SUCCESS; +} + +static RTEXITCODE deleteCloudImage(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + HRESULT hrc = S_OK; + + static const RTGETOPTDEF s_aOptions[] = + { + { "--id", 'i', RTGETOPT_REQ_STRING }, + { "help", 'h', RTGETOPT_REQ_NOTHING }, + { "--help", 'h', RTGETOPT_REQ_NOTHING } + }; + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + if (a->argc == iFirst) + { + RTPrintf(Cloud::tr("Empty command parameter list, show help.\n")); + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + } + + Utf8Str strImageId; + + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'i': + { + if (strImageId.isNotEmpty()) + return errorArgument(Cloud::tr("Duplicate parameter: --id")); + + strImageId = ValueUnion.psz; + if (strImageId.isEmpty()) + return errorArgument(Cloud::tr("Empty parameter: --id")); + + break; + } + + case 'h': + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + /* Delayed check. It allows us to print help information.*/ + hrc = checkAndSetCommonOptions(a, pCommonOpts); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + if (strImageId.isEmpty()) + return errorArgument(Cloud::tr("Missing parameter: --id")); + + + ComPtr pCloudProfile = pCommonOpts->profile.pCloudProfile; + + ComObjPtr oCloudClient; + CHECK_ERROR2_RET(hrc, pCloudProfile, + CreateCloudClient(oCloudClient.asOutParam()), + RTEXITCODE_FAILURE); + RTPrintf(Cloud::tr("Deleting cloud image with id %s...\n"), strImageId.c_str()); + + ComPtr progress; + CHECK_ERROR2_RET(hrc, oCloudClient, + DeleteImage(Bstr(strImageId).raw(), progress.asOutParam()), + RTEXITCODE_FAILURE); + hrc = showProgress(progress); + CHECK_PROGRESS_ERROR_RET(progress, (Cloud::tr("Deleting cloud image failed")), RTEXITCODE_FAILURE); + + if (SUCCEEDED(hrc)) + RTPrintf(Cloud::tr("Cloud image was deleted successfully\n")); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +static RTEXITCODE handleCloudImage(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + enum + { + kCloudImageIota = 1000, + kCloudImage_Create, + kCloudImage_Delete, + kCloudImage_Export, + kCloudImage_Import, + kCloudImage_Info, + kCloudImage_Update, + }; + + static const RTGETOPTDEF s_aOptions[] = + { + { "create", kCloudImage_Create, RTGETOPT_REQ_NOTHING }, + { "delete", kCloudImage_Delete, RTGETOPT_REQ_NOTHING }, + { "export", kCloudImage_Export, RTGETOPT_REQ_NOTHING }, + { "import", kCloudImage_Import, RTGETOPT_REQ_NOTHING }, + { "info", kCloudImage_Info, RTGETOPT_REQ_NOTHING }, + { "update", kCloudImage_Update, RTGETOPT_REQ_NOTHING }, + + { "help", 'h', RTGETOPT_REQ_NOTHING }, + { "-?", 'h', RTGETOPT_REQ_NOTHING }, + { "-help", 'h', RTGETOPT_REQ_NOTHING }, + { "--help", 'h', RTGETOPT_REQ_NOTHING }, + }; + + if (a->argc == iFirst) + { + RTPrintf(Cloud::tr("Empty command parameter list, show help.\n")); + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + } + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + int c; + RTGETOPTUNION ValueUnion; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + /* Sub-commands: */ + case kCloudImage_Create: + setCurrentSubcommand(HELP_SCOPE_CLOUDIMAGE_CREATE); + return createCloudImage(a, GetState.iNext, pCommonOpts); + + case kCloudImage_Export: + setCurrentSubcommand(HELP_SCOPE_CLOUDIMAGE_EXPORT); + return exportCloudImage(a, GetState.iNext, pCommonOpts); + + case kCloudImage_Import: + setCurrentSubcommand(HELP_SCOPE_CLOUDIMAGE_IMPORT); + return importCloudImage(a, GetState.iNext, pCommonOpts); + + case kCloudImage_Info: + setCurrentSubcommand(HELP_SCOPE_CLOUDIMAGE_INFO); + return showCloudImageInfo(a, GetState.iNext, pCommonOpts); + + case kCloudImage_Update: +// setCurrentSubcommand(HELP_SCOPE_CLOUDIMAGE_UPDATE); + return updateCloudImage(a, GetState.iNext, pCommonOpts); + + case kCloudImage_Delete: + setCurrentSubcommand(HELP_SCOPE_CLOUDIMAGE_DELETE); + return deleteCloudImage(a, GetState.iNext, pCommonOpts); + + case 'h': + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + return errorNoSubcommand(); +} + +#ifdef VBOX_WITH_CLOUD_NET +struct CloudNetworkOptions +{ + BOOL fEnable; + BOOL fDisable; + Bstr strNetworkId; + Bstr strNetworkName; +}; +typedef struct CloudNetworkOptions CLOUDNETOPT; +typedef CLOUDNETOPT *PCLOUDNETOPT; + +static RTEXITCODE createUpdateCloudNetworkCommon(ComPtr cloudNetwork, CLOUDNETOPT& options, PCLOUDCOMMONOPT pCommonOpts) +{ + HRESULT hrc = S_OK; + + Bstr strProvider = pCommonOpts->provider.pszProviderName; + Bstr strProfile = pCommonOpts->profile.pszProfileName; + + if (options.fEnable) + { + CHECK_ERROR2_RET(hrc, cloudNetwork, COMSETTER(Enabled)(TRUE), RTEXITCODE_FAILURE); + } + if (options.fDisable) + { + CHECK_ERROR2_RET(hrc, cloudNetwork, COMSETTER(Enabled)(FALSE), RTEXITCODE_FAILURE); + } + if (options.strNetworkId.isNotEmpty()) + { + CHECK_ERROR2_RET(hrc, cloudNetwork, COMSETTER(NetworkId)(options.strNetworkId.raw()), RTEXITCODE_FAILURE); + } + if (strProvider.isNotEmpty()) + { + CHECK_ERROR2_RET(hrc, cloudNetwork, COMSETTER(Provider)(strProvider.raw()), RTEXITCODE_FAILURE); + } + if (strProfile.isNotEmpty()) + { + CHECK_ERROR2_RET(hrc, cloudNetwork, COMSETTER(Profile)(strProfile.raw()), RTEXITCODE_FAILURE); + } + + return RTEXITCODE_SUCCESS; +} + + +static RTEXITCODE createCloudNetwork(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + HRESULT hrc = S_OK; + hrc = checkAndSetCommonOptions(a, pCommonOpts); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + /* Required parameters, the rest is handled in update */ + static const RTGETOPTDEF s_aOptions[] = + { + { "--disable", 'd', RTGETOPT_REQ_NOTHING }, + { "--enable", 'e', RTGETOPT_REQ_NOTHING }, + { "--network-id", 'i', RTGETOPT_REQ_STRING }, + { "--name", 'n', RTGETOPT_REQ_STRING }, + }; + + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + CLOUDNETOPT options; + options.fEnable = FALSE; + options.fDisable = FALSE; + + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'd': + options.fDisable = TRUE; + break; + case 'e': + options.fEnable = TRUE; + break; + case 'i': + options.strNetworkId=ValueUnion.psz; + break; + case 'n': + options.strNetworkName=ValueUnion.psz; + break; + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + default: + return errorGetOpt(c, &ValueUnion); + } + } + + if (options.strNetworkName.isEmpty()) + return errorArgument(Cloud::tr("Missing --name parameter")); + if (options.strNetworkId.isEmpty()) + return errorArgument(Cloud::tr("Missing --network-id parameter")); + + ComPtr pVirtualBox = a->virtualBox; + + ComPtr cloudNetwork; + CHECK_ERROR2_RET(hrc, pVirtualBox, + CreateCloudNetwork(options.strNetworkName.raw(), cloudNetwork.asOutParam()), + RTEXITCODE_FAILURE); + + /* Fill out the created network */ + RTEXITCODE rc = createUpdateCloudNetworkCommon(cloudNetwork, options, pCommonOpts); + if (RT_SUCCESS(rc)) + RTPrintf(Cloud::tr("Cloud network was created successfully\n")); + + return rc; +} + + +static RTEXITCODE showCloudNetworkInfo(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + RT_NOREF(pCommonOpts); + HRESULT hrc = S_OK; + static const RTGETOPTDEF s_aOptions[] = + { + { "--name", 'n', RTGETOPT_REQ_STRING }, + }; + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + Bstr strNetworkName; + + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'n': + strNetworkName=ValueUnion.psz; + break; + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + default: + return errorGetOpt(c, &ValueUnion); + } + } + + if (strNetworkName.isEmpty()) + return errorArgument(Cloud::tr("Missing --name parameter")); + + ComPtr pVirtualBox = a->virtualBox; + ComPtr cloudNetwork; + CHECK_ERROR2_RET(hrc, pVirtualBox, + FindCloudNetworkByName(strNetworkName.raw(), cloudNetwork.asOutParam()), + RTEXITCODE_FAILURE); + + RTPrintf(Cloud::tr("Name: %ls\n"), strNetworkName.raw()); + BOOL fEnabled = FALSE; + cloudNetwork->COMGETTER(Enabled)(&fEnabled); + RTPrintf(Cloud::tr("State: %s\n"), fEnabled ? Cloud::tr("Enabled") : Cloud::tr("Disabled")); + Bstr Provider; + cloudNetwork->COMGETTER(Provider)(Provider.asOutParam()); + RTPrintf(Cloud::tr("CloudProvider: %ls\n"), Provider.raw()); + Bstr Profile; + cloudNetwork->COMGETTER(Profile)(Profile.asOutParam()); + RTPrintf(Cloud::tr("CloudProfile: %ls\n"), Profile.raw()); + Bstr NetworkId; + cloudNetwork->COMGETTER(NetworkId)(NetworkId.asOutParam()); + RTPrintf(Cloud::tr("CloudNetworkId: %ls\n"), NetworkId.raw()); + Bstr netName = BstrFmt("cloud-%ls", strNetworkName.raw()); + RTPrintf(Cloud::tr("VBoxNetworkName: %ls\n\n"), netName.raw()); + + return RTEXITCODE_SUCCESS; +} + + +static RTEXITCODE updateCloudNetwork(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + HRESULT hrc = S_OK; + + static const RTGETOPTDEF s_aOptions[] = + { + { "--disable", 'd', RTGETOPT_REQ_NOTHING }, + { "--enable", 'e', RTGETOPT_REQ_NOTHING }, + { "--network-id", 'i', RTGETOPT_REQ_STRING }, + { "--name", 'n', RTGETOPT_REQ_STRING }, + }; + + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + CLOUDNETOPT options; + options.fEnable = FALSE; + options.fDisable = FALSE; + + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'd': + options.fDisable = TRUE; + break; + case 'e': + options.fEnable = TRUE; + break; + case 'i': + options.strNetworkId=ValueUnion.psz; + break; + case 'n': + options.strNetworkName=ValueUnion.psz; + break; + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + default: + return errorGetOpt(c, &ValueUnion); + } + } + + if (options.strNetworkName.isEmpty()) + return errorArgument(Cloud::tr("Missing --name parameter")); + + ComPtr pVirtualBox = a->virtualBox; + ComPtr cloudNetwork; + CHECK_ERROR2_RET(hrc, pVirtualBox, + FindCloudNetworkByName(options.strNetworkName.raw(), cloudNetwork.asOutParam()), + RTEXITCODE_FAILURE); + + RTEXITCODE rc = createUpdateCloudNetworkCommon(cloudNetwork, options, pCommonOpts); + if (RT_SUCCESS(rc)) + RTPrintf(Cloud::tr("Cloud network %ls was updated successfully\n"), options.strNetworkName.raw()); + + return rc; +} + + +static RTEXITCODE deleteCloudNetwork(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + RT_NOREF(pCommonOpts); + HRESULT hrc = S_OK; + static const RTGETOPTDEF s_aOptions[] = + { + { "--name", 'n', RTGETOPT_REQ_STRING }, + }; + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + Bstr strNetworkName; + + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'n': + strNetworkName=ValueUnion.psz; + break; + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + default: + return errorGetOpt(c, &ValueUnion); + } + } + + if (strNetworkName.isEmpty()) + return errorArgument(Cloud::tr("Missing --name parameter")); + + ComPtr pVirtualBox = a->virtualBox; + ComPtr cloudNetwork; + CHECK_ERROR2_RET(hrc, pVirtualBox, + FindCloudNetworkByName(strNetworkName.raw(), cloudNetwork.asOutParam()), + RTEXITCODE_FAILURE); + + CHECK_ERROR2_RET(hrc, pVirtualBox, + RemoveCloudNetwork(cloudNetwork), + RTEXITCODE_FAILURE); + + if (SUCCEEDED(hrc)) + RTPrintf(Cloud::tr("Cloud network %ls was deleted successfully\n"), strNetworkName.raw()); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + + +static RTEXITCODE setupCloudNetworkEnv(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + RT_NOREF(pCommonOpts); + HRESULT hrc = S_OK; + static const RTGETOPTDEF s_aOptions[] = + { + { "--gateway-os-name", 'n', RTGETOPT_REQ_STRING }, + { "--gateway-os-version", 'v', RTGETOPT_REQ_STRING }, + { "--gateway-shape", 's', RTGETOPT_REQ_STRING }, + { "--tunnel-network-name", 't', RTGETOPT_REQ_STRING }, + { "--tunnel-network-range", 'r', RTGETOPT_REQ_STRING }, + { "--compartment-id", 'c', RTGETOPT_REQ_STRING } + }; + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + Bstr strGatewayOsName; + Bstr strGatewayOsVersion; + Bstr strGatewayShape; + Bstr strTunnelNetworkName; + Bstr strTunnelNetworkRange; + Bstr strCompartmentId; + + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'n': + strGatewayOsName=ValueUnion.psz; + break; + case 'v': + strGatewayOsVersion=ValueUnion.psz; + break; + case 's': + strGatewayShape=ValueUnion.psz; + break; + case 't': + strTunnelNetworkName=ValueUnion.psz; + break; + case 'r': + strTunnelNetworkRange=ValueUnion.psz; + break; + case 'c': + strCompartmentId=ValueUnion.psz; + break; + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + default: + return errorGetOpt(c, &ValueUnion); + } + } + + /* Delayed check. It allows us to print help information.*/ + hrc = checkAndSetCommonOptions(a, pCommonOpts); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + ComPtr pVirtualBox = a->virtualBox; + + RTPrintf(Cloud::tr("Setting up tunnel network in the cloud...\n")); + + ComPtr pCloudProfile = pCommonOpts->profile.pCloudProfile; + + /* Use user-specified profile instead of default one. */ + if (strCompartmentId.isNotEmpty()) + { + CHECK_ERROR2_RET(hrc, pCloudProfile, + SetProperty(Bstr("compartment").raw(), Bstr(strCompartmentId).raw()), + RTEXITCODE_FAILURE); + } + + ComObjPtr oCloudClient; + CHECK_ERROR2_RET(hrc, pCloudProfile, + CreateCloudClient(oCloudClient.asOutParam()), + RTEXITCODE_FAILURE); + + ComPtr cloudNetworkEnv; + ComPtr progress; + CHECK_ERROR2_RET(hrc, oCloudClient, + SetupCloudNetworkEnvironment(strTunnelNetworkName.raw(), strTunnelNetworkRange.raw(), + strGatewayOsName.raw(), strGatewayOsVersion.raw(), strGatewayShape.raw(), + cloudNetworkEnv.asOutParam(), progress.asOutParam()), + RTEXITCODE_FAILURE); + + hrc = showProgress(progress); + CHECK_PROGRESS_ERROR_RET(progress, (Cloud::tr("Setting up cloud network environment failed")), RTEXITCODE_FAILURE); + + Bstr tunnelNetworkId; + hrc = cloudNetworkEnv->COMGETTER(TunnelNetworkId)(tunnelNetworkId.asOutParam()); + RTPrintf(Cloud::tr("Cloud network environment was set up successfully. Tunnel network id is: %ls\n"), tunnelNetworkId.raw()); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + + +static RTEXITCODE handleCloudNetwork(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts) +{ + enum + { + kCloudNetworkIota = 1000, + kCloudNetwork_Create, + kCloudNetwork_Delete, + kCloudNetwork_Info, + kCloudNetwork_Setup, + kCloudNetwork_Update, + }; + + static const RTGETOPTDEF s_aOptions[] = + { + { "create", kCloudNetwork_Create, RTGETOPT_REQ_NOTHING }, + { "delete", kCloudNetwork_Delete, RTGETOPT_REQ_NOTHING }, + { "info", kCloudNetwork_Info, RTGETOPT_REQ_NOTHING }, + { "setup", kCloudNetwork_Setup, RTGETOPT_REQ_NOTHING }, + { "update", kCloudNetwork_Update, RTGETOPT_REQ_NOTHING }, + }; + + if (a->argc < 1) + return errorNoSubcommand(); + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + int c; + RTGETOPTUNION ValueUnion; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + /* Sub-commands: */ + case kCloudNetwork_Create: + return createCloudNetwork(a, GetState.iNext, pCommonOpts); + + case kCloudNetwork_Info: + return showCloudNetworkInfo(a, GetState.iNext, pCommonOpts); + + case kCloudNetwork_Update: + return updateCloudNetwork(a, GetState.iNext, pCommonOpts); + + case kCloudNetwork_Delete: + return deleteCloudNetwork(a, GetState.iNext, pCommonOpts); + + case kCloudNetwork_Setup: + return setupCloudNetworkEnv(a, GetState.iNext, pCommonOpts); + + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + return errorNoSubcommand(); +} +#endif /* VBOX_WITH_CLOUD_NET */ + + +RTEXITCODE handleCloud(HandlerArg *a) +{ + enum + { + kCloudIota = 1000, + kCloud_Image, + kCloud_Instance, + kCloud_List, + kCloud_Machine, + kCloud_Network, + kCloud_Object, + kCloud_ShowVMInfo, + kCloud_Volume, + }; + + static const RTGETOPTDEF s_aOptions[] = + { + /* common options */ + { "--provider", 'v', RTGETOPT_REQ_STRING }, + { "--profile", 'f', RTGETOPT_REQ_STRING }, + + { "image", kCloud_Image, RTGETOPT_REQ_NOTHING }, + { "instance", kCloud_Instance, RTGETOPT_REQ_NOTHING }, + { "list", kCloud_List, RTGETOPT_REQ_NOTHING }, + { "machine", kCloud_Machine, RTGETOPT_REQ_NOTHING }, + { "network", kCloud_Network, RTGETOPT_REQ_NOTHING }, + { "object", kCloud_Object, RTGETOPT_REQ_NOTHING }, + { "showvminfo", kCloud_ShowVMInfo, RTGETOPT_REQ_NOTHING }, + { "volume", kCloud_Volume, RTGETOPT_REQ_NOTHING }, + }; + + if (a->argc < 1) + return errorNoSubcommand(); + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + CLOUDCOMMONOPT commonOpts = { {NULL, NULL}, {NULL, NULL} }; + int c; + RTGETOPTUNION ValueUnion; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'v': // --provider + commonOpts.provider.pszProviderName = ValueUnion.psz; + break; + + case 'f': // --profile + commonOpts.profile.pszProfileName = ValueUnion.psz; + break; + + /* Sub-commands: */ + case kCloud_List: + return handleCloudLists(a, GetState.iNext, &commonOpts); + + case kCloud_Image: + return handleCloudImage(a, GetState.iNext, &commonOpts); + + case kCloud_Instance: + return handleCloudInstance(a, GetState.iNext, &commonOpts); + +#ifdef VBOX_WITH_CLOUD_NET + case kCloud_Network: + return handleCloudNetwork(a, GetState.iNext, &commonOpts); +#endif /* VBOX_WITH_CLOUD_NET */ + + /* "cloud machine ..." handling is in VBoxManageCloudMachine.cpp */ + case kCloud_Machine: + return handleCloudMachine(a, GetState.iNext, + commonOpts.provider.pszProviderName, + commonOpts.profile.pszProfileName); + + /* ... including aliases that mimic the local vm commands */ + case kCloud_ShowVMInfo: + return handleCloudShowVMInfo(a, GetState.iNext, + commonOpts.provider.pszProviderName, + commonOpts.profile.pszProfileName); + + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + return errorNoSubcommand(); +} diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageCloudMachine.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageCloudMachine.cpp new file mode 100644 index 00000000..78223337 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageCloudMachine.cpp @@ -0,0 +1,1482 @@ +/* $Id: VBoxManageCloudMachine.cpp $ */ +/** @file + * VBoxManageCloudMachine - The cloud machine related commands. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "VBoxManage.h" + +#include + +#include +#include +#include + +#include +#include + +DECLARE_TRANSLATION_CONTEXT(CloudMachine); + + +struct CMachineHandlerArg + : public HandlerArg +{ + ComPtr pClient; + + const char *pcszSpec; /* RTGETOPTUNION::psz, points inside argv */ + enum { GUESS, ID, NAME } enmSpecKind; + ComPtr pMachine; + + explicit CMachineHandlerArg(const HandlerArg &a) + : HandlerArg(a), pcszSpec(NULL), enmSpecKind(GUESS) {} +}; + + +static int selectCloudProvider(ComPtr &pProvider, + const ComPtr &pVirtualBox, + const char *pszProviderName); +static int selectCloudProfile(ComPtr &pProfile, + const ComPtr &pProvider, + const char *pszProviderName); +static int getCloudClient(CMachineHandlerArg &a, + const char *pcszProviderName, + const char *pcszProfileName); + +static HRESULT getMachineList(com::SafeIfaceArray &aMachines, + const ComPtr &pClient); + +static HRESULT getMachineBySpec(CMachineHandlerArg *a); +static HRESULT getMachineById(CMachineHandlerArg *a); +static HRESULT getMachineByName(CMachineHandlerArg *a); +static HRESULT getMachineByGuess(CMachineHandlerArg *a); + +static int checkMachineSpecArgument(CMachineHandlerArg *a, + int ch, const RTGETOPTUNION &Val); + + +static RTEXITCODE handleCloudMachineImpl(CMachineHandlerArg *a, int iFirst); + +static RTEXITCODE handleCloudMachineStart(CMachineHandlerArg *a, int iFirst); +static RTEXITCODE handleCloudMachineReboot(CMachineHandlerArg *a, int iFirst); +static RTEXITCODE handleCloudMachineReset(CMachineHandlerArg *a, int iFirst); +static RTEXITCODE handleCloudMachineShutdown(CMachineHandlerArg *a, int iFirst); +static RTEXITCODE handleCloudMachinePowerdown(CMachineHandlerArg *a, int iFirst); +static RTEXITCODE handleCloudMachineTerminate(CMachineHandlerArg *a, int iFirst); + +static RTEXITCODE handleCloudMachineConsoleHistory(CMachineHandlerArg *a, int iFirst); + +static RTEXITCODE listCloudMachinesImpl(CMachineHandlerArg *a, int iFirst); +static RTEXITCODE handleCloudMachineInfo(CMachineHandlerArg *a, int iFirst); + +static HRESULT printMachineInfo(const ComPtr &pMachine); +static HRESULT printFormValue(const ComPtr &pValue); + + + +/* + * This is a temporary hack as I don't want to refactor "cloud" + * handling right now, as it's not yet clear to me what is the + * direction that we want to take with it. + * + * The problem with the way "cloud" command handling is currently + * written is that it's a bit schizophrenic about whether we have + * multiple cloud providers or not. OTOH it insists on --provider + * being mandatory, on the other it hardcodes the list of available + * subcommands, though in principle those can vary from provider to + * provider. If we do want to support multiple providers we might + * need to come up with a way to allow an extpack provider to supply + * its own VBoxManage command handler for "cloud" based on --provider + * as the selector. + * + * Processing of --provider and --profile should not be postponed + * until the leaf command handler, but rather happen immediately, so + * do this here at our earliest opportunity (without actually doing it + * in handleCloud). + */ +RTEXITCODE +handleCloudMachine(HandlerArg *a, int iFirst, + const char *pcszProviderName, + const char *pcszProfileName) +{ + CMachineHandlerArg handlerArg(*a); + int vrc = getCloudClient(handlerArg, pcszProviderName, pcszProfileName); + if (RT_FAILURE(vrc)) + return RTEXITCODE_FAILURE; + + return handleCloudMachineImpl(&handlerArg, iFirst); +} + + +/* + * Select cloud provider to use based on the --provider option to the + * "cloud" command. The option is not mandatory if only a single + * provider is available. + */ +static int +selectCloudProvider(ComPtr &pProvider, + const ComPtr &pVirtualBox, + const char *pcszProviderName) +{ + HRESULT hrc; + + ComPtr pCloudProviderManager; + CHECK_ERROR2_RET(hrc, pVirtualBox, + COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()), + VERR_GENERAL_FAILURE); + + + /* + * If the provider is explicitly specified, just look it up and + * return. + */ + if (pcszProviderName != NULL) + { + /* + * Should we also provide a way to specify the provider also + * by its id? Is it even useful? If so, should we use a + * different option or check if the provider name looks like + * an id and used a different getter? + */ + CHECK_ERROR2_RET(hrc, pCloudProviderManager, + GetProviderByShortName(com::Bstr(pcszProviderName).raw(), + pProvider.asOutParam()), + VERR_NOT_FOUND); + + return VINF_SUCCESS; + } + + + /* + * We have only one provider and it's not clear if we will ever + * have more than one. Forcing the user to explicitly specify the + * only provider available is not very nice. So try to be + * friendly. + */ + com::SafeIfaceArray aProviders; + CHECK_ERROR2_RET(hrc, pCloudProviderManager, + COMGETTER(Providers)(ComSafeArrayAsOutParam(aProviders)), + VERR_GENERAL_FAILURE); + + if (aProviders.size() == 0) + { + RTMsgError(CloudMachine::tr("cloud: no providers available")); + return VERR_NOT_FOUND; + } + + if (aProviders.size() > 1) + { + RTMsgError(CloudMachine::tr("cloud: multiple providers available," + " '--provider' option is required")); + return VERR_MISSING; + } + + /* Do RTMsgInfo telling the user which one was selected? */ + pProvider = aProviders[0]; + return VINF_SUCCESS; +} + + +/* + * Select cloud profile to use based on the --profile option to the + * "cloud" command. The option is not mandatory if only a single + * profile exists. + */ +static int +selectCloudProfile(ComPtr &pProfile, + const ComPtr &pProvider, + const char *pcszProfileName) +{ + HRESULT hrc; + + /* + * If the profile is explicitly specified, just look it up and + * return. + */ + if (pcszProfileName != NULL) + { + CHECK_ERROR2_RET(hrc, pProvider, + GetProfileByName(com::Bstr(pcszProfileName).raw(), + pProfile.asOutParam()), + VERR_NOT_FOUND); + + return VINF_SUCCESS; + } + + + /* + * If the user has just one profile for this provider, don't force + * them to specify it. I'm not entirely sure about this one, + * actually. It's nice for interactive use, but it might be not + * forward compatible if used in a script and then when another + * profile is created the script starts failing. I'd say, give + * them enough rope... + */ + com::SafeIfaceArray aProfiles; + CHECK_ERROR2_RET(hrc, pProvider, + COMGETTER(Profiles)(ComSafeArrayAsOutParam(aProfiles)), + VERR_GENERAL_FAILURE); + + if (aProfiles.size() == 0) + { + RTMsgError(CloudMachine::tr("cloud: no profiles exist")); + return VERR_NOT_FOUND; + } + + if (aProfiles.size() > 1) + { + RTMsgError(CloudMachine::tr("cloud: multiple profiles exist, '--profile' option is required")); + return VERR_MISSING; + } + + /* Do RTMsgInfo telling the user which one was selected? */ + pProfile = aProfiles[0]; + return VINF_SUCCESS; +} + + +static int +getCloudClient(CMachineHandlerArg &a, + const char *pcszProviderName, + const char *pcszProfileName) +{ + ComPtr pProvider; + int vrc = selectCloudProvider(pProvider, a.virtualBox, pcszProviderName); + if (RT_FAILURE(vrc)) + return vrc; + + ComPtr pProfile; + vrc = selectCloudProfile(pProfile, pProvider, pcszProfileName); + if (RT_FAILURE(vrc)) + return vrc; + + ComPtr pCloudClient; + CHECK_ERROR2I_RET(pProfile, CreateCloudClient(pCloudClient.asOutParam()), VERR_GENERAL_FAILURE); + + a.pClient = pCloudClient; + return VINF_SUCCESS; +} + + +static HRESULT +getMachineList(com::SafeIfaceArray &aMachines, + const ComPtr &pClient) +{ + HRESULT hrc; + + ComPtr pListProgress; + CHECK_ERROR2_RET(hrc, pClient, + ReadCloudMachineList(pListProgress.asOutParam()), + hrc); + + hrc = showProgress(pListProgress, SHOW_PROGRESS_NONE); + if (FAILED(hrc)) + return hrc; + + CHECK_ERROR2_RET(hrc, pClient, + COMGETTER(CloudMachineList)(ComSafeArrayAsOutParam(aMachines)), + hrc); + + return S_OK; +} + + +static HRESULT +getMachineById(CMachineHandlerArg *a) +{ + HRESULT hrc; + + ComPtr pMachine; + CHECK_ERROR2_RET(hrc, a->pClient, + GetCloudMachine(com::Bstr(a->pcszSpec).raw(), + pMachine.asOutParam()), hrc); + + ComPtr pRefreshProgress; + CHECK_ERROR2_RET(hrc, pMachine, + Refresh(pRefreshProgress.asOutParam()), hrc); + + hrc = showProgress(pRefreshProgress, SHOW_PROGRESS_NONE); + if (FAILED(hrc)) + return hrc; + + a->pMachine = pMachine; + return S_OK; +} + + +static HRESULT +getMachineByName(CMachineHandlerArg *a) +{ + HRESULT hrc; + + com::SafeIfaceArray aMachines; + hrc = getMachineList(aMachines, a->pClient); + if (FAILED(hrc)) + return hrc; + + const size_t cMachines = aMachines.size(); + if (cMachines == 0) + return VBOX_E_OBJECT_NOT_FOUND; + + ComPtr pMachineFound; + for (size_t i = 0; i < cMachines; ++i) + { + const ComPtr pMachine = aMachines[i]; + + com::Bstr bstrName; + CHECK_ERROR2_RET(hrc, pMachine, + COMGETTER(Name)(bstrName.asOutParam()), + hrc); + + if (!bstrName.equals(a->pcszSpec)) + continue; + + if (pMachineFound.isNull()) + pMachineFound = pMachine; + else + { + com::Bstr bstrId1, bstrId2; + CHECK_ERROR2_RET(hrc, pMachineFound, + COMGETTER(Id)(bstrId1.asOutParam()), + hrc); + CHECK_ERROR2_RET(hrc, pMachine, + COMGETTER(Id)(bstrId2.asOutParam()), + hrc); + + RTMsgError(CloudMachine::tr("ambiguous name: %ls and %ls"), bstrId1.raw(), bstrId2.raw()); + return VBOX_E_OBJECT_NOT_FOUND; + } + } + + if (pMachineFound.isNull()) + return VBOX_E_OBJECT_NOT_FOUND; + + a->pMachine = pMachineFound; + return S_OK; +} + + +/* + * Try to find the machine refered by pcszWhatever. If the look up by + * id fails we might want to fallback to look up by name, b/c someone + * might want to use a uuid as a display name of a machine. But cloud + * lookups are not fast, so that would be incurring performance + * penalty for typos or for machines that are gone. Should provide + * explicit --id/--name options instead. + */ +static HRESULT +getMachineByGuess(CMachineHandlerArg *a) +{ + HRESULT hrc; + + RTUUID Uuid; + int vrc = RTUuidFromStr(&Uuid, a->pcszSpec); + if (RT_SUCCESS(vrc)) + hrc = getMachineById(a); + else + hrc = getMachineByName(a); + + if (FAILED(hrc)) + return hrc; + + return S_OK; +} + + + +/* + * RTGETOPTINIT_FLAGS_NO_STD_OPTS recognizes both --help and --version + * and we don't want the latter. It's easier to add one line of this + * macro to the s_aOptions initializers than to filter out --version. + */ +#define CLOUD_MACHINE_RTGETOPTDEF_HELP \ + { "--help", 'h', RTGETOPT_REQ_NOTHING }, \ + { "-help", 'h', RTGETOPT_REQ_NOTHING }, \ + { "help", 'h', RTGETOPT_REQ_NOTHING }, \ + { "-?", 'h', RTGETOPT_REQ_NOTHING } + +static RTEXITCODE +errThereCanBeOnlyOne() +{ + return RTMsgErrorExit(RTEXITCODE_SYNTAX, + CloudMachine::tr("only one machine can be specified")); +} + + +#define CLOUD_MACHINE_RTGETOPTDEF_MACHINE \ + { "--id", 'i', RTGETOPT_REQ_STRING }, \ + { "--name", 'n', RTGETOPT_REQ_STRING } + + +/* + * Almost all the cloud machine commands take a machine argument, so + * factor out the code to fish it out from the command line. + * + * ch - option should be processed by the caller. + * VINF_SUCCESS - option was processed. + * VERR_PARSE_ERROR - RTEXITCODE_SYNTAX + * Other IPRT errors - RTEXITCODE_FAILURE + */ +static int +checkMachineSpecArgument(CMachineHandlerArg *a, + int ch, const RTGETOPTUNION &Val) +{ + int vrc; + + switch (ch) + { + /* + * Note that we don't used RTGETOPT_REQ_UUID here as it would + * be too limiting. First, we need the original string for + * the API call, not the UUID, and second, if the UUID has bad + * forward RTGetOptPrintError doesn't have access to the + * option argument for the error message. So do the format + * check ourselves. + */ + case 'i': /* --id */ + { + const char *pcszId = Val.psz; + + if (a->pcszSpec != NULL) + { + errThereCanBeOnlyOne(); + return VERR_PARSE_ERROR; + } + + RTUUID Uuid; + vrc = RTUuidFromStr(&Uuid, pcszId); + if (RT_FAILURE(vrc)) + { + RTMsgError(CloudMachine::tr("not a valid uuid: %s"), pcszId); + return VERR_PARSE_ERROR; + } + + a->pcszSpec = pcszId; + a->enmSpecKind = CMachineHandlerArg::ID; + return VINF_SUCCESS; + } + + case 'n': /* --name */ + { + const char *pcszName = Val.psz; + + if (a->pcszSpec != NULL) + { + errThereCanBeOnlyOne(); + return VERR_PARSE_ERROR; + } + + a->pcszSpec = pcszName; + a->enmSpecKind = CMachineHandlerArg::NAME; + return VINF_SUCCESS; + } + + /* + * Plain word (no dash/es). This must name a machine, though + * we have to guess whether it's an id or a name. + */ + case VINF_GETOPT_NOT_OPTION: + { + const char *pcszNameOrId = Val.psz; + + if (a->pcszSpec != NULL) + { + errThereCanBeOnlyOne(); + return VERR_PARSE_ERROR; + } + + a->pcszSpec = pcszNameOrId; + a->enmSpecKind = CMachineHandlerArg::GUESS; + return VINF_SUCCESS; + } + + /* might as well do it here */ + case 'h': /* --help */ + { + printHelp(g_pStdOut); + return VINF_CALLBACK_RETURN; + } + } + + /* let the caller deal with it */ + return VINF_NOT_SUPPORTED; +} + + +static HRESULT +getMachineBySpec(CMachineHandlerArg *a) +{ + HRESULT hrc = E_FAIL; + + if (a->pcszSpec == NULL) + { + RTMsgErrorExit(RTEXITCODE_SYNTAX, CloudMachine::tr("machine not specified")); + return E_FAIL; + } + + if (a->pcszSpec[0] == '\0') + { + RTMsgError(CloudMachine::tr("machine name is empty")); + return E_FAIL; + } + + switch (a->enmSpecKind) + { + case CMachineHandlerArg::ID: + hrc = getMachineById(a); + if (FAILED(hrc)) + { + if (hrc == VBOX_E_OBJECT_NOT_FOUND) + RTMsgError(CloudMachine::tr("unable to find machine with id %s"), a->pcszSpec); + return hrc; + } + break; + + case CMachineHandlerArg::NAME: + hrc = getMachineByName(a); + if (FAILED(hrc)) + { + if (hrc == VBOX_E_OBJECT_NOT_FOUND) + RTMsgError(CloudMachine::tr("unable to find machine with name %s"), a->pcszSpec); + return hrc; + } + break; + + case CMachineHandlerArg::GUESS: + hrc = getMachineByGuess(a); + if (FAILED(hrc)) + { + if (hrc == VBOX_E_OBJECT_NOT_FOUND) + RTMsgError(CloudMachine::tr("unable to find machine %s"), a->pcszSpec); + return hrc; + } + break; + } + + /* switch was exhaustive (and successful) */ + AssertReturn(SUCCEEDED(hrc), E_FAIL); + return S_OK; +} + + + + +/* + * cloud machine [--id id | --name name] command ... + * + * We allow machine to be specified after "machine" but only with an + * explicit option for the obvious reason. We will also check for + * these options and machine spec as a plain words argument after the + * command word, so user can use either of: + * + * cloud machine --name foo start + * cloud machine start --name foo + * cloud machine start foo + * + * This will accept e.g. cloud machine --name foo list ... b/c we + * don't yet know that it's "list" that is coming, so commands that + * don't take machine argument check that separately when called. One + * side effect of this is that specifying several machines or using a + * syntactically invalid id will be reported as such, not as an + * unknown option, but that's a relatively minor nit. + */ +static RTEXITCODE +handleCloudMachineImpl(CMachineHandlerArg *a, int iFirst) +{ + enum + { + kMachineIota = 1000, + kMachine_ConsoleHistory, + kMachine_Info, + kMachine_List, + kMachine_Powerdown, + kMachine_Reboot, + kMachine_Reset, + kMachine_Shutdown, + kMachine_Start, + kMachine_Terminate, + }; + + // setCurrentSubcommand(HELP_SCOPE_CLOUD_MACHINE); + static const RTGETOPTDEF s_aOptions[] = + { + { "console-history", kMachine_ConsoleHistory, RTGETOPT_REQ_NOTHING }, + { "consolehistory", kMachine_ConsoleHistory, RTGETOPT_REQ_NOTHING }, + { "info", kMachine_Info, RTGETOPT_REQ_NOTHING }, + { "list", kMachine_List, RTGETOPT_REQ_NOTHING }, + { "powerdown", kMachine_Powerdown, RTGETOPT_REQ_NOTHING }, + { "reboot", kMachine_Reboot, RTGETOPT_REQ_NOTHING }, + { "reset", kMachine_Reset, RTGETOPT_REQ_NOTHING }, + { "shutdown", kMachine_Shutdown, RTGETOPT_REQ_NOTHING }, + { "start", kMachine_Start, RTGETOPT_REQ_NOTHING }, + { "terminate", kMachine_Terminate, RTGETOPT_REQ_NOTHING }, + CLOUD_MACHINE_RTGETOPTDEF_MACHINE, + CLOUD_MACHINE_RTGETOPTDEF_HELP + }; + + RTGETOPTSTATE OptState; + int vrc = RTGetOptInit(&OptState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), + iFirst, RTGETOPTINIT_FLAGS_NO_STD_OPTS); + AssertRCReturn(vrc, RTMsgErrorExit(RTEXITCODE_INIT, CloudMachine::tr("cloud machine: RTGetOptInit: %Rra"), vrc)); + + int ch; + RTGETOPTUNION Val; + while ((ch = RTGetOpt(&OptState, &Val)) != 0) + { + if (RT_FAILURE(ch)) + return RTGetOptPrintError(ch, &Val); + + /* + * Check for an unknown word first: checkMachineSpecArgument() + * would try to interpret that as a machine id/name. + */ + if (ch == VINF_GETOPT_NOT_OPTION) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, + CloudMachine::tr("Invalid sub-command: %s"), Val.psz); + + /* + * Allow --id/--name after "machine", before the command. + * Also handles --help. + */ + vrc = checkMachineSpecArgument(a, ch, Val); + if (vrc == VINF_SUCCESS) + continue; + if (vrc == VINF_CALLBACK_RETURN) + return RTEXITCODE_SUCCESS; + if (vrc == VERR_PARSE_ERROR) + return RTEXITCODE_SYNTAX; + + /* + * Dispatch to command implementation ([ab]use getopt to do + * string comparisons for us). + */ + switch (ch) + { + case kMachine_ConsoleHistory: + return handleCloudMachineConsoleHistory(a, OptState.iNext); + + case kMachine_Info: + return handleCloudMachineInfo(a, OptState.iNext); + + case kMachine_List: + return listCloudMachinesImpl(a, OptState.iNext); + + case kMachine_Powerdown: + return handleCloudMachinePowerdown(a, OptState.iNext); + + case kMachine_Reboot: + return handleCloudMachineReboot(a, OptState.iNext); + + case kMachine_Reset: + return handleCloudMachineReset(a, OptState.iNext); + + case kMachine_Shutdown: + return handleCloudMachineShutdown(a, OptState.iNext); + + case kMachine_Start: + return handleCloudMachineStart(a, OptState.iNext); + + case kMachine_Terminate: + return handleCloudMachineTerminate(a, OptState.iNext); + + default: /* should never happen */ + return RTMsgErrorExit(RTEXITCODE_INIT, + CloudMachine::tr("cloud machine: internal error: %d"), ch); + } + } + + return RTMsgErrorExit(RTEXITCODE_SYNTAX, + CloudMachine::tr("cloud machine: command required\n" + "Try '--help' for more information.")); +} + + +/* + * cloud list machines + * + * The "cloud list" prefix handling is in VBoxManageCloud.cpp, so this + * function is not static. See handleCloudMachine() for the + * explanation early provider/profile lookup. + */ +RTEXITCODE +listCloudMachines(HandlerArg *a, int iFirst, + const char *pcszProviderName, + const char *pcszProfileName) +{ + CMachineHandlerArg handlerArg(*a); + int vrc = getCloudClient(handlerArg, pcszProviderName, pcszProfileName); + if (RT_FAILURE(vrc)) + return RTEXITCODE_FAILURE; + + return listCloudMachinesImpl(&handlerArg, iFirst); +} + + +/* + * cloud machine list # convenience alias + * cloud list machines # see above + */ +static RTEXITCODE +listCloudMachinesImpl(CMachineHandlerArg *a, int iFirst) +{ + // setCurrentSubcommand(HELP_SCOPE_CLOUD_MACHINE_LIST); + static const RTGETOPTDEF s_aOptions[] = + { + { "--long", 'l', RTGETOPT_REQ_NOTHING }, + { "--sort", 's', RTGETOPT_REQ_NOTHING }, + CLOUD_MACHINE_RTGETOPTDEF_HELP + }; + + enum kFormatEnum { kFormat_Short, kFormat_Long }; + kFormatEnum enmFormat = kFormat_Short; + + enum kSortOrderEnum { kSortOrder_None, kSortOrder_Name, kSortOrder_Id }; + kSortOrderEnum enmSortOrder = kSortOrder_None; + + if (a->pcszSpec != NULL) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, + CloudMachine::tr("cloud machine list: unexpected machine argument")); + + + RTGETOPTSTATE OptState; + int vrc = RTGetOptInit(&OptState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), + iFirst, RTGETOPTINIT_FLAGS_NO_STD_OPTS); + AssertRCReturn(vrc, RTMsgErrorExit(RTEXITCODE_INIT, CloudMachine::tr("cloud machine list: RTGetOptInit: %Rra"), vrc)); + + int ch; + RTGETOPTUNION Val; + while ((ch = RTGetOpt(&OptState, &Val)) != 0) + { + switch (ch) + { + case 'l': + enmFormat = kFormat_Long; + break; + + case 's': + /** @todo optional argument to select the sort key? */ + enmSortOrder = kSortOrder_Name; + break; + + case 'h': /* --help */ + printHelp(g_pStdOut); + return RTEXITCODE_SUCCESS; + + + case VINF_GETOPT_NOT_OPTION: + return RTMsgErrorExit(RTEXITCODE_SYNTAX, + CloudMachine::tr("Invalid sub-command: %s"), Val.psz); + + default: + return RTGetOptPrintError(ch, &Val); + } + } + + com::SafeIfaceArray aMachines; + HRESULT hrc = getMachineList(aMachines, a->pClient); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + const size_t cMachines = aMachines.size(); + if (cMachines == 0) + return RTEXITCODE_SUCCESS; + + + /* + * Get names/ids that we need for the short output and to sort the + * list. + */ + std::vector > vMachines(cMachines); + std::vector vBstrNames(cMachines); + std::vector vBstrIds(cMachines); + for (size_t i = 0; i < cMachines; ++i) + { + vMachines[i] = aMachines[i]; + + CHECK_ERROR2_RET(hrc, vMachines[i], + COMGETTER(Name)(vBstrNames[i].asOutParam()), + RTEXITCODE_FAILURE); + + CHECK_ERROR2_RET(hrc, vMachines[i], + COMGETTER(Id)(vBstrIds[i].asOutParam()), + RTEXITCODE_FAILURE); + } + + + /* + * Sort the list if necessary. The sort is indirect via an + * intermediate array of indexes. + */ + std::vector vIndexes(cMachines); + for (size_t i = 0; i < cMachines; ++i) + vIndexes[i] = i; + + if (enmSortOrder != kSortOrder_None) + { + struct SortBy { + const std::vector &ks; + SortBy(const std::vector &aKeys) : ks(aKeys) {} + bool operator() (size_t l, size_t r) { return ks[l] < ks[r]; } + }; + + std::sort(vIndexes.begin(), vIndexes.end(), + SortBy(enmSortOrder == kSortOrder_Name + ? vBstrNames : vBstrIds)); + } + + + if (enmFormat == kFormat_Short) + { + for (size_t i = 0; i < cMachines; ++i) + { + const size_t idx = vIndexes[i]; + const com::Bstr &bstrId = vBstrIds[idx]; + const com::Bstr &bstrName = vBstrNames[idx]; + + RTPrintf("%ls %ls\n", bstrId.raw(), bstrName.raw()); + } + } + else // kFormat_Long + { + for (size_t i = 0; i < cMachines; ++i) + { + const size_t idx = vIndexes[i]; + const ComPtr &pMachine = vMachines[idx]; + + if (i != 0) + RTPrintf("\n"); + printMachineInfo(pMachine); + } + } + + return RTEXITCODE_SUCCESS; +} + + +/* + * cloud showvminfo "id" + * + * Alias for "cloud machine info" that tries to match the local vm + * counterpart. + */ +RTEXITCODE +handleCloudShowVMInfo(HandlerArg *a, int iFirst, + const char *pcszProviderName, + const char *pcszProfileName) +{ + CMachineHandlerArg handlerArg(*a); + int vrc = getCloudClient(handlerArg, pcszProviderName, pcszProfileName); + if (RT_FAILURE(vrc)) + return RTEXITCODE_FAILURE; + + return handleCloudMachineInfo(&handlerArg, iFirst); +} + + +/* + * cloud machine info "id" ... + */ +static RTEXITCODE +handleCloudMachineInfo(CMachineHandlerArg *a, int iFirst) +{ + enum + { + kMachineInfoIota = 1000, + kMachineInfo_Details, + }; + + // setCurrentSubcommand(HELP_SCOPE_CLOUD_MACHINE_INFO); + static const RTGETOPTDEF s_aOptions[] = + { + { "--details", kMachineInfo_Details, RTGETOPT_REQ_NOTHING }, + CLOUD_MACHINE_RTGETOPTDEF_MACHINE, + CLOUD_MACHINE_RTGETOPTDEF_HELP + }; + + RTGETOPTSTATE OptState; + int vrc = RTGetOptInit(&OptState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), + iFirst, RTGETOPTINIT_FLAGS_NO_STD_OPTS); + AssertRCReturn(vrc, RTMsgErrorExit(RTEXITCODE_INIT, "RTGetOptInit: %Rra", vrc)); + + int ch; + RTGETOPTUNION Val; + while ((ch = RTGetOpt(&OptState, &Val)) != 0) + { + vrc = checkMachineSpecArgument(a, ch, Val); + if (vrc == VINF_SUCCESS) + continue; + if (vrc == VINF_CALLBACK_RETURN) + return RTEXITCODE_SUCCESS; + if (vrc == VERR_PARSE_ERROR) + return RTEXITCODE_SYNTAX; + + switch (ch) + { + case kMachineInfo_Details: + /* currently no-op */ + break; + + default: + return RTGetOptPrintError(ch, &Val); + } + } + + HRESULT hrc = getMachineBySpec(a); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + /* end of boilerplate */ + + + hrc = printMachineInfo(a->pMachine); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + return RTEXITCODE_SUCCESS; +} + + +static HRESULT +printMachineInfo(const ComPtr &pMachine) +{ + HRESULT hrc; + + com::Bstr bstrId; + CHECK_ERROR2_RET(hrc, pMachine, + COMGETTER(Id)(bstrId.asOutParam()), + hrc); + RTPrintf("UUID: %ls\n", bstrId.raw()); + + + /* + * Check if the machine is accessible and print the error + * message if not. + */ + BOOL fAccessible = FALSE; + CHECK_ERROR2_RET(hrc, pMachine, + COMGETTER(Accessible)(&fAccessible), hrc); + + if (!fAccessible) + { + RTMsgError(CloudMachine::tr("machine is not accessible")); // XXX: Id? + + ComPtr pErrorInfo; + CHECK_ERROR2_RET(hrc, pMachine, + COMGETTER(AccessError)(pErrorInfo.asOutParam()), hrc); + + while (!pErrorInfo.isNull()) + { + com::Bstr bstrText; + CHECK_ERROR2_RET(hrc, pErrorInfo, + COMGETTER(Text)(bstrText.asOutParam()), hrc); + RTStrmPrintf(g_pStdErr, "%ls\n", bstrText.raw()); + + CHECK_ERROR2_RET(hrc, pErrorInfo, + COMGETTER(Next)(pErrorInfo.asOutParam()), hrc); + } + + return E_FAIL; + } + + + /* + * The machine seems to be ok, print its details. + */ + CloudMachineState_T enmState; + CHECK_ERROR2_RET(hrc, pMachine, + COMGETTER(State)(&enmState), + hrc); + switch (enmState) { + case CloudMachineState_Invalid: + RTPrintf(CloudMachine::tr("State: Invalid (%RU32)\n"), CloudMachineState_Invalid); + break; + + case CloudMachineState_Provisioning: + RTPrintf(CloudMachine::tr("State: Provisioning (%RU32)\n"), CloudMachineState_Provisioning); + break; + + case CloudMachineState_Running: + RTPrintf(CloudMachine::tr("State: Running (%RU32)\n"), CloudMachineState_Running); + break; + + case CloudMachineState_Starting: + RTPrintf(CloudMachine::tr("State: Starting (%RU32)\n"), CloudMachineState_Starting); + break; + + case CloudMachineState_Stopping: + RTPrintf(CloudMachine::tr("State: Stopping (%RU32)\n"), CloudMachineState_Stopping); + break; + + case CloudMachineState_Stopped: + RTPrintf(CloudMachine::tr("State: Stopped (%RU32)\n"), CloudMachineState_Stopped); + break; + + case CloudMachineState_CreatingImage: + RTPrintf(CloudMachine::tr("State: CreatingImage (%RU32)\n"), CloudMachineState_CreatingImage); + break; + + case CloudMachineState_Terminating: + RTPrintf(CloudMachine::tr("State: Terminating (%RU32)\n"), CloudMachineState_Terminating); + break; + + case CloudMachineState_Terminated: + RTPrintf(CloudMachine::tr("State: Terminated (%RU32)\n"), CloudMachineState_Terminated); + break; + + default: + RTPrintf(CloudMachine::tr("State: Unknown state (%RU32)\n"), enmState); + } + + ComPtr pDetails; + CHECK_ERROR2_RET(hrc, pMachine, + GetDetailsForm(pDetails.asOutParam()), hrc); + + if (RT_UNLIKELY(pDetails.isNull())) + { + RTMsgError(CloudMachine::tr("null details")); /* better error message? */ + return E_FAIL; + } + + com::SafeIfaceArray aValues; + CHECK_ERROR2_RET(hrc, pDetails, + COMGETTER(Values)(ComSafeArrayAsOutParam(aValues)), hrc); + for (size_t i = 0; i < aValues.size(); ++i) + { + hrc = printFormValue(aValues[i]); + if (FAILED(hrc)) + return hrc; + } + + return S_OK; +} + + +static HRESULT +printFormValue(const ComPtr &pValue) +{ + HRESULT hrc; + + BOOL fVisible = FALSE; + CHECK_ERROR2_RET(hrc, pValue, + COMGETTER(Visible)(&fVisible), hrc); + if (!fVisible) + return S_OK; + + + com::Bstr bstrLabel; + CHECK_ERROR2_RET(hrc, pValue, + COMGETTER(Label)(bstrLabel.asOutParam()), hrc); + + FormValueType_T enmType; + CHECK_ERROR2_RET(hrc, pValue, + COMGETTER(Type)(&enmType), hrc); + + switch (enmType) + { + case FormValueType_Boolean: + { + ComPtr pBoolValue; + hrc = pValue.queryInterfaceTo(pBoolValue.asOutParam()); + if (FAILED(hrc)) + { + RTStrmPrintf(g_pStdErr, + CloudMachine::tr("%ls: unable to convert to boolean value\n"), + bstrLabel.raw()); + break; + } + + BOOL fSelected; + hrc = pBoolValue->GetSelected(&fSelected); + if (FAILED(hrc)) + { + RTStrmPrintf(g_pStdOut, + "%ls: %Rhra", bstrLabel.raw(), hrc); + break; + } + + RTPrintf("%ls: %RTbool\n", + bstrLabel.raw(), RT_BOOL(fSelected)); + break; + } + + case FormValueType_String: + { + ComPtr pStrValue; + hrc = pValue.queryInterfaceTo(pStrValue.asOutParam()); + if (FAILED(hrc)) + { + RTStrmPrintf(g_pStdErr, + CloudMachine::tr("%ls: unable to convert to string value\n"), + bstrLabel.raw()); + break; + } + + /* + * GUI hack: if clipboard string is set, it contains + * untruncated long value, usually full OCID, so check it + * first. Make this selectable with an option? + */ + com::Bstr bstrValue; + hrc = pStrValue->COMGETTER(ClipboardString)(bstrValue.asOutParam()); + if (FAILED(hrc)) + { + RTStrmPrintf(g_pStdOut, + "%ls: %Rhra", bstrLabel.raw(), hrc); + break; + } + + if (bstrValue.isEmpty()) + { + hrc = pStrValue->GetString(bstrValue.asOutParam()); + if (FAILED(hrc)) + { + RTStrmPrintf(g_pStdOut, + "%ls: %Rhra", bstrLabel.raw(), hrc); + break; + } + } + + RTPrintf("%ls: %ls\n", + bstrLabel.raw(), bstrValue.raw()); + break; + } + + case FormValueType_RangedInteger: + { + ComPtr pIntValue; + hrc = pValue.queryInterfaceTo(pIntValue.asOutParam()); + if (FAILED(hrc)) + { + RTStrmPrintf(g_pStdErr, + CloudMachine::tr("%ls: unable to convert to integer value\n"), + bstrLabel.raw()); + break; + } + + LONG lValue; + hrc = pIntValue->GetInteger(&lValue); + if (FAILED(hrc)) + { + RTStrmPrintf(g_pStdOut, + "%ls: %Rhra", bstrLabel.raw(), hrc); + break; + } + + RTPrintf("%ls: %RI64\n", + bstrLabel.raw(), (int64_t)lValue); + break; + } + + case FormValueType_Choice: + { + ComPtr pChoiceValue; + hrc = pValue.queryInterfaceTo(pChoiceValue.asOutParam()); + if (FAILED(hrc)) + { + RTStrmPrintf(g_pStdErr, + CloudMachine::tr("%ls: unable to convert to choice value\n"), + bstrLabel.raw()); + break; + } + + com::SafeArray aValues; + hrc = pChoiceValue->COMGETTER(Values)(ComSafeArrayAsOutParam(aValues)); + if (FAILED(hrc)) + { + RTStrmPrintf(g_pStdOut, + CloudMachine::tr("%ls: values: %Rhra"), + bstrLabel.raw(), hrc); + break; + } + + LONG idxSelected = -1; + hrc = pChoiceValue->GetSelectedIndex(&idxSelected); + if (FAILED(hrc)) + { + RTStrmPrintf(g_pStdOut, + CloudMachine::tr("%ls: selectedIndex: %Rhra"), + bstrLabel.raw(), hrc); + break; + } + + if (idxSelected < 0 || (size_t)idxSelected > aValues.size()) + { + RTStrmPrintf(g_pStdOut, + CloudMachine::tr("%ls: selected index %RI64 out of range [0, %zu)\n"), + bstrLabel.raw(), (int64_t)idxSelected, aValues.size()); + break; + } + + RTPrintf("%ls: %ls\n", + bstrLabel.raw(), aValues[idxSelected]); + break; + } + + default: + { + RTStrmPrintf(g_pStdOut, CloudMachine::tr("unknown value type %RU32\n"), enmType); + break; + } + } + + return S_OK; +} + + +/* + * Boilerplate code to get machine by name/id from the arguments. + * Shared by action subcommands b/c they currently don't have any + * extra options (but we can't use this for e.g. "info" that has + * --details). + */ +static RTEXITCODE +getMachineFromArgs(CMachineHandlerArg *a, int iFirst) +{ + static const RTGETOPTDEF s_aOptions[] = + { + CLOUD_MACHINE_RTGETOPTDEF_MACHINE, + CLOUD_MACHINE_RTGETOPTDEF_HELP + }; + + RTGETOPTSTATE OptState; + int vrc = RTGetOptInit(&OptState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), + iFirst, RTGETOPTINIT_FLAGS_NO_STD_OPTS); + AssertRCReturn(vrc, RTMsgErrorExit(RTEXITCODE_INIT, /* internal error */ "RTGetOptInit: %Rra", vrc)); + + int ch; + RTGETOPTUNION Val; + while ((ch = RTGetOpt(&OptState, &Val)) != 0) + { + vrc = checkMachineSpecArgument(a, ch, Val); + if (vrc == VINF_SUCCESS) + continue; + if (vrc == VINF_CALLBACK_RETURN) + return RTEXITCODE_SUCCESS; + if (vrc == VERR_PARSE_ERROR) + return RTEXITCODE_SYNTAX; + + switch (ch) + { + /* no other options currently */ + default: + return RTGetOptPrintError(ch, &Val); + } + } + + HRESULT hrc = getMachineBySpec(a); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + return RTEXITCODE_SUCCESS; +} + + +/* + * cloud machine start "id" + */ +static RTEXITCODE +handleCloudMachineStart(CMachineHandlerArg *a, int iFirst) +{ + HRESULT hrc; + + // setCurrentSubcommand(HELP_SCOPE_CLOUD_MACHINE_START); + RTEXITCODE status = getMachineFromArgs(a, iFirst); + if (status != RTEXITCODE_SUCCESS) + return status; + + + ComPtr pProgress; + CHECK_ERROR2_RET(hrc, a->pMachine, + PowerUp(pProgress.asOutParam()), + RTEXITCODE_FAILURE); + + hrc = showProgress(pProgress, SHOW_PROGRESS_NONE); + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + + +/* + * cloud machine reboot "id" + * "Press" ACPI power button, then power the instance back up. + */ +static RTEXITCODE +handleCloudMachineReboot(CMachineHandlerArg *a, int iFirst) +{ + HRESULT hrc; + + // setCurrentSubcommand(HELP_SCOPE_CLOUD_MACHINE_REBOOT); + RTEXITCODE status = getMachineFromArgs(a, iFirst); + if (status != RTEXITCODE_SUCCESS) + return status; + + + ComPtr pProgress; + CHECK_ERROR2_RET(hrc, a->pMachine, + Reboot(pProgress.asOutParam()), + RTEXITCODE_FAILURE); + + hrc = showProgress(pProgress, SHOW_PROGRESS_NONE); + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + + +/* + * cloud machine reset "id" + * Force power down machine, then power the instance back up. + */ +static RTEXITCODE +handleCloudMachineReset(CMachineHandlerArg *a, int iFirst) +{ + HRESULT hrc; + + // setCurrentSubcommand(HELP_SCOPE_CLOUD_MACHINE_RESET); + RTEXITCODE status = getMachineFromArgs(a, iFirst); + if (status != RTEXITCODE_SUCCESS) + return status; + + + ComPtr pProgress; + CHECK_ERROR2_RET(hrc, a->pMachine, + Reset(pProgress.asOutParam()), + RTEXITCODE_FAILURE); + + hrc = showProgress(pProgress, SHOW_PROGRESS_NONE); + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + + +/* + * cloud machine shutdown "id" + * "Press" ACPI power button. + */ +static RTEXITCODE +handleCloudMachineShutdown(CMachineHandlerArg *a, int iFirst) +{ + HRESULT hrc; + + // setCurrentSubcommand(HELP_SCOPE_CLOUD_MACHINE_SHUTDOWN); + RTEXITCODE status = getMachineFromArgs(a, iFirst); + if (status != RTEXITCODE_SUCCESS) + return status; + + + ComPtr pProgress; + CHECK_ERROR2_RET(hrc, a->pMachine, + Shutdown(pProgress.asOutParam()), + RTEXITCODE_FAILURE); + + hrc = showProgress(pProgress, SHOW_PROGRESS_NONE); + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + + +/* + * cloud machine powerdown "id" + * Yank the power cord. + */ +static RTEXITCODE +handleCloudMachinePowerdown(CMachineHandlerArg *a, int iFirst) +{ + HRESULT hrc; + + // setCurrentSubcommand(HELP_SCOPE_CLOUD_MACHINE_POWERDOWN); + RTEXITCODE status = getMachineFromArgs(a, iFirst); + if (status != RTEXITCODE_SUCCESS) + return status; + + + ComPtr pProgress; + CHECK_ERROR2_RET(hrc, a->pMachine, + PowerDown(pProgress.asOutParam()), + RTEXITCODE_FAILURE); + + hrc = showProgress(pProgress, SHOW_PROGRESS_NONE); + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + + +/* + * cloud machine terminate "id" + * Discard the instance running this machine. + */ +static RTEXITCODE +handleCloudMachineTerminate(CMachineHandlerArg *a, int iFirst) +{ + HRESULT hrc; + + // setCurrentSubcommand(HELP_SCOPE_CLOUD_MACHINE_TERMINATE); + RTEXITCODE status = getMachineFromArgs(a, iFirst); + if (status != RTEXITCODE_SUCCESS) + return status; + + + ComPtr pProgress; + CHECK_ERROR2_RET(hrc, a->pMachine, + Terminate(pProgress.asOutParam()), + RTEXITCODE_FAILURE); + + hrc = showProgress(pProgress, SHOW_PROGRESS_NONE); + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + + +/* + * cloud machine console-history "id" + */ +static RTEXITCODE +handleCloudMachineConsoleHistory(CMachineHandlerArg *a, int iFirst) +{ + HRESULT hrc; + + // setCurrentSubcommand(HELP_SCOPE_CLOUD_MACHINE_CONSOLEHISTORY); + RTEXITCODE status = getMachineFromArgs(a, iFirst); + if (status != RTEXITCODE_SUCCESS) + return status; + + + ComPtr pHistoryStream; + ComPtr pHistoryProgress; + CHECK_ERROR2_RET(hrc, a->pMachine, + GetConsoleHistory(pHistoryStream.asOutParam(), + pHistoryProgress.asOutParam()), + RTEXITCODE_FAILURE); + + hrc = showProgress(pHistoryProgress, SHOW_PROGRESS_NONE); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + bool fEOF = false; + while (!fEOF) + { + com::SafeArray aChunk; + CHECK_ERROR2_RET(hrc, pHistoryStream, + Read(64 *_1K, 0, ComSafeArrayAsOutParam(aChunk)), + RTEXITCODE_FAILURE); + if (aChunk.size() == 0) + break; + + RTStrmWrite(g_pStdOut, aChunk.raw(), aChunk.size()); + } + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageControlVM.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageControlVM.cpp new file mode 100644 index 00000000..98a3f8f2 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageControlVM.cpp @@ -0,0 +1,2287 @@ +/* $Id: VBoxManageControlVM.cpp $ */ +/** @file + * VBoxManage - Implementation of the controlvm command. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "VBoxManage.h" +#include "VBoxManageUtils.h" + +#include + +DECLARE_TRANSLATION_CONTEXT(ControlVM); + +VMProcPriority_T nameToVMProcPriority(const char *pszName); + +/** + * 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 vrc = RTStrToUInt32Ex(psz, &pszNext, 10, &u32); + if ( RT_SUCCESS(vrc) + && *pszNext == '\0' + && u32 >= 1 + && u32 <= cMaxNum) + return (unsigned)u32; + errorArgument(ControlVM::tr("Invalid %s number '%s'."), name, psz); + 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 &llScancodes) +{ + /* Send scancodes to the VM. */ + com::SafeArray saScancodes(llScancodes); + + HRESULT hrc = S_OK; + size_t i; + for (i = 0; i < saScancodes.size(); ++i) + { + hrc = pKeyboard->PutScancode(saScancodes[i]); + if (FAILED(hrc)) + { + RTMsgError(ControlVM::tr("Failed to send a scancode.")); + break; + } + + RTThreadSleep(10); /* "Typing" too fast causes lost characters. */ + } + + return hrc; +} + +static void keyboardCharsToScancodes(const char *pch, size_t cchMax, std::list &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 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 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 = RTFileQuerySize(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(ControlVM::tr("Out of memory allocating %d bytes.", "", cbBuffer), cbBuffer); + } + else + RTMsgError(ControlVM::tr("File size %RI64 is greater than %RI64: '%s'."), cbFile, cbFileMax, pszFilename); + } + else + RTMsgError(ControlVM::tr("Cannot get size of file '%s': %Rrc."), pszFilename, vrc); + + RTFileClose(File); + } + else + RTMsgError(ControlVM::tr("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 hrc; + + if (a->argc < 2) + return errorSyntax(ControlVM::tr("Not enough parameters.")); + + /* try to find the given machine */ + ComPtr machine; + CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(), + machine.asOutParam())); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + /* open a session for the VM */ + CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE); + + ComPtr console; + ComPtr sessionMachine; + + do + { + /* get the associated console */ + CHECK_ERROR_BREAK(a->session, COMGETTER(Console)(console.asOutParam())); + if (!console) + return RTMsgErrorExit(RTEXITCODE_FAILURE, ControlVM::tr("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")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_PAUSE); + CHECK_ERROR_BREAK(console, Pause()); + } + else if (!strcmp(a->argv[1], "resume")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RESUME); + CHECK_ERROR_BREAK(console, Resume()); + } + else if (!strcmp(a->argv[1], "reset")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RESET); + CHECK_ERROR_BREAK(console, Reset()); + } + else if (!strcmp(a->argv[1], "unplugcpu")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_UNPLUGCPU); + if (a->argc <= 1 + 1) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + + unsigned n = parseNum(a->argv[2], 32, "CPU"); + + CHECK_ERROR_BREAK(sessionMachine, HotUnplugCPU(n)); + } + else if (!strcmp(a->argv[1], "plugcpu")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_PLUGCPU); + if (a->argc <= 1 + 1) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + + unsigned n = parseNum(a->argv[2], 32, "CPU"); + + CHECK_ERROR_BREAK(sessionMachine, HotPlugCPU(n)); + } + else if (!strcmp(a->argv[1], "cpuexecutioncap")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_CPUEXECUTIONCAP); + if (a->argc <= 1 + 1) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]); + hrc = 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")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_AUDIOIN); + + ComPtr audioSettings; + CHECK_ERROR_BREAK(sessionMachine, COMGETTER(AudioSettings)(audioSettings.asOutParam())); + ComPtr adapter; + CHECK_ERROR_BREAK(audioSettings, COMGETTER(Adapter)(adapter.asOutParam())); + if (adapter) + { + bool fEnabled; + if (RT_FAILURE(parseBool(a->argv[2], &fEnabled))) + { + errorSyntax(ControlVM::tr("Invalid value '%s'."), a->argv[2]); + hrc = E_FAIL; + break; + } + CHECK_ERROR_RET(adapter, COMSETTER(EnabledIn)(fEnabled), RTEXITCODE_FAILURE); + fNeedsSaving = true; + } + else + { + errorSyntax(ControlVM::tr("Audio adapter not enabled in VM configuration.")); + hrc = E_FAIL; + break; + } + } + else if (!strcmp(a->argv[1], "audioout")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_AUDIOOUT); + + ComPtr audioSettings; + CHECK_ERROR_BREAK(sessionMachine, COMGETTER(AudioSettings)(audioSettings.asOutParam())); + ComPtr adapter; + CHECK_ERROR_BREAK(audioSettings, COMGETTER(Adapter)(adapter.asOutParam())); + if (adapter) + { + bool fEnabled; + if (RT_FAILURE(parseBool(a->argv[2], &fEnabled))) + { + errorSyntax(ControlVM::tr("Invalid value '%s'."), a->argv[2]); + hrc = E_FAIL; + break; + } + CHECK_ERROR_RET(adapter, COMSETTER(EnabledOut)(fEnabled), RTEXITCODE_FAILURE); + fNeedsSaving = true; + } + else + { + errorSyntax(ControlVM::tr("Audio adapter not enabled in VM configuration.")); + hrc = E_FAIL; + break; + } + } +#ifdef VBOX_WITH_SHARED_CLIPBOARD + else if (!strcmp(a->argv[1], "clipboard")) + { + if (a->argc <= 1 + 1) + { + errorArgument(ControlVM::tr("Missing argument to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + + ClipboardMode_T mode = ClipboardMode_Disabled; /* Shut up MSC */ + if (!strcmp(a->argv[2], "mode")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_CLIPBOARD_MODE); + if (a->argc <= 1 + 2) + { + errorSyntax(ControlVM::tr("Missing argument to '%s %s'."), a->argv[1], a->argv[2]); + hrc = E_FAIL; + break; + } + + if (!strcmp(a->argv[3], "disabled")) + mode = ClipboardMode_Disabled; + else if (!strcmp(a->argv[3], "hosttoguest")) + mode = ClipboardMode_HostToGuest; + else if (!strcmp(a->argv[3], "guesttohost")) + mode = ClipboardMode_GuestToHost; + else if (!strcmp(a->argv[3], "bidirectional")) + mode = ClipboardMode_Bidirectional; + else + { + errorSyntax(ControlVM::tr("Invalid '%s %s' argument '%s'."), a->argv[1], a->argv[2], a->argv[3]); + hrc = E_FAIL; + break; + } + + CHECK_ERROR_BREAK(sessionMachine, COMSETTER(ClipboardMode)(mode)); + if (SUCCEEDED(hrc)) + fNeedsSaving = true; + } +# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS + else if (!strcmp(a->argv[2], "filetransfers")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_CLIPBOARD_FILETRANSFERS); + if (a->argc <= 1 + 2) + { + errorSyntax(ControlVM::tr("Missing argument to '%s %s'."), a->argv[1], a->argv[2]); + hrc = E_FAIL; + break; + } + + bool fEnabled; + if (RT_FAILURE(parseBool(a->argv[3], &fEnabled))) + { + errorSyntax(ControlVM::tr("Invalid '%s %s' argument '%s'."), a->argv[1], a->argv[2], a->argv[3]); + hrc = E_FAIL; + break; + } + + CHECK_ERROR_BREAK(sessionMachine, COMSETTER(ClipboardFileTransfersEnabled)(fEnabled)); + fNeedsSaving = true; + } +# endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */ + else + { + errorArgument(ControlVM::tr("Invalid '%s' argument '%s'."), a->argv[1], a->argv[2]); + hrc = E_FAIL; + break; + } + } +#endif /* VBOX_WITH_SHARED_CLIPBOARD */ + else if (!strcmp(a->argv[1], "draganddrop")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_DRAGANDDROP); + if (a->argc <= 1 + 1) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]); + hrc = 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 + { + errorSyntax(ControlVM::tr("Invalid '%s' argument '%s'."), a->argv[1], a->argv[2]); + hrc = E_FAIL; + } + if (SUCCEEDED(hrc)) + { + CHECK_ERROR_BREAK(sessionMachine, COMSETTER(DnDMode)(mode)); + if (SUCCEEDED(hrc)) + fNeedsSaving = true; + } + } + else if (!strcmp(a->argv[1], "poweroff")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_POWEROFF); + ComPtr progress; + CHECK_ERROR_BREAK(console, PowerDown(progress.asOutParam())); + + hrc = showProgress(progress); + CHECK_PROGRESS_ERROR(progress, (ControlVM::tr("Failed to power off machine."))); + } + else if (!strcmp(a->argv[1], "savestate")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_SAVESTATE); + /* first pause so we don't trigger a live save which needs more time/resources */ + bool fPaused = false; + hrc = console->Pause(); + if (FAILED(hrc)) + { + bool fError = true; + if (hrc == 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 */ + hrc = VBOX_E_INVALID_VM_STATE; + if (machineState != MachineState_Paused) + { + RTMsgError(ControlVM::tr("Machine in invalid state %d -- %s."), + machineState, machineStateToName(machineState, false)); + } + else + { + fError = false; + fPaused = true; + } + } + if (fError) + break; + } + + ComPtr progress; + CHECK_ERROR(sessionMachine, SaveState(progress.asOutParam())); + if (FAILED(hrc)) + { + if (!fPaused) + console->Resume(); + break; + } + + hrc = showProgress(progress); + CHECK_PROGRESS_ERROR(progress, (ControlVM::tr("Failed to save machine state."))); + if (FAILED(hrc)) + { + if (!fPaused) + console->Resume(); + } + } + else if (!strcmp(a->argv[1], "acpipowerbutton")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_ACPIPOWERBUTTON); + CHECK_ERROR_BREAK(console, PowerButton()); + } + else if (!strcmp(a->argv[1], "acpisleepbutton")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_ACPISLEEPBUTTON); + CHECK_ERROR_BREAK(console, SleepButton()); + } +#ifdef VBOX_WITH_GUEST_CONTROL + else if ( !strcmp(a->argv[1], "reboot") + || !strcmp(a->argv[1], "shutdown")) /* With shutdown we mean gracefully powering off the VM by letting the guest OS do its thing. */ + { + const bool fReboot = !strcmp(a->argv[1], "reboot"); + if (fReboot) + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_REBOOT); + else + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_SHUTDOWN); + + ComPtr pGuest; + CHECK_ERROR_BREAK(console, COMGETTER(Guest)(pGuest.asOutParam())); + if (!pGuest) + { + RTMsgError(ControlVM::tr("Guest not running.")); + hrc = E_FAIL; + break; + } + + com::SafeArray aShutdownFlags; + if (fReboot) + aShutdownFlags.push_back(GuestShutdownFlag_Reboot); + else + aShutdownFlags.push_back(GuestShutdownFlag_PowerOff); + + if ( a->argc >= 3 + && !strcmp(a->argv[2], "--force")) + aShutdownFlags.push_back(GuestShutdownFlag_Force); + + CHECK_ERROR(pGuest, Shutdown(ComSafeArrayAsInParam(aShutdownFlags))); + if (FAILED(hrc)) + { + if (hrc == VBOX_E_NOT_SUPPORTED) + { + if (fReboot) + RTMsgError(ControlVM::tr("Current installed Guest Additions don't support rebooting the guest.")); + else + RTMsgError(ControlVM::tr("Current installed Guest Additions don't support shutting down the guest.")); + } + } + } +#endif + else if (!strcmp(a->argv[1], "keyboardputscancode")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_KEYBOARDPUTSCANCODE); + ComPtr pKeyboard; + CHECK_ERROR_BREAK(console, COMGETTER(Keyboard)(pKeyboard.asOutParam())); + if (!pKeyboard) + { + RTMsgError(ControlVM::tr("Guest not running.")); + hrc = E_FAIL; + break; + } + + if (a->argc <= 1 + 1) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'. Expected IBM PC AT set 2 keyboard scancode(s)."), + a->argv[1]); + hrc = E_FAIL; + break; + } + + std::list 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 vrc = RTStrToUInt8Ex(a->argv[i], NULL, 16, &u8Scancode); + if (RT_FAILURE (vrc)) + { + RTMsgError(ControlVM::tr("Converting '%s' returned %Rrc!"), a->argv[i], vrc); + hrc = E_FAIL; + break; + } + + llScancodes.push_back(u8Scancode); + } + else + { + RTMsgError(ControlVM::tr("'%s' is not a hex byte!"), a->argv[i]); + hrc = E_FAIL; + break; + } + } + + if (FAILED(hrc)) + break; + + hrc = keyboardPutScancodes(pKeyboard, llScancodes); + } + else if (!strcmp(a->argv[1], "keyboardputstring")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_KEYBOARDPUTSTRING); + ComPtr pKeyboard; + CHECK_ERROR_BREAK(console, COMGETTER(Keyboard)(pKeyboard.asOutParam())); + if (!pKeyboard) + { + RTMsgError(ControlVM::tr("Guest not running.")); + hrc = E_FAIL; + break; + } + + if (a->argc <= 1 + 1) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'. Expected ASCII string(s)."), a->argv[1]); + hrc = E_FAIL; + break; + } + + hrc = keyboardPutString(pKeyboard, a->argc, a->argv); + } + else if (!strcmp(a->argv[1], "keyboardputfile")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_KEYBOARDPUTFILE); + ComPtr pKeyboard; + CHECK_ERROR_BREAK(console, COMGETTER(Keyboard)(pKeyboard.asOutParam())); + if (!pKeyboard) + { + RTMsgError(ControlVM::tr("Guest not running.")); + hrc = E_FAIL; + break; + } + + if (a->argc <= 1 + 1) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + + hrc = keyboardPutFile(pKeyboard, a->argv[2]); + } + else if (!strncmp(a->argv[1], "setlinkstate", 12)) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_SETLINKSTATE); + /* Get the number of network adapters */ + ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine); + unsigned n = parseNum(&a->argv[1][12], NetworkAdapterCount, "NIC"); + if (!n) + { + hrc = E_FAIL; + break; + } + if (a->argc <= 1 + 1) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + /* get the corresponding network adapter */ + ComPtr adapter; + CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam())); + if (adapter) + { + bool fEnabled; + if (RT_FAILURE(parseBool(a->argv[2], &fEnabled))) + { + errorSyntax(ControlVM::tr("Invalid link state '%s'."), a->argv[2]); + hrc = E_FAIL; + break; + } + CHECK_ERROR_BREAK(adapter, COMSETTER(CableConnected)(fEnabled)); + 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)) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NICTRACEFILE); + /* Get the number of network adapters */ + ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine); + unsigned n = parseNum(&a->argv[1][12], NetworkAdapterCount, "NIC"); + if (!n) + { + hrc = E_FAIL; + break; + } + if (a->argc <= 2) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + + /* get the corresponding network adapter */ + ComPtr 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 + { + errorSyntax(ControlVM::tr("Filename not specified for NIC %lu."), n); + hrc = E_FAIL; + break; + } + if (SUCCEEDED(hrc)) + fNeedsSaving = true; + } + else + RTMsgError(ControlVM::tr("The NIC %d is currently disabled and thus its tracefile can't be changed."), n); + } + } + else if (!strncmp(a->argv[1], "nictrace", 8)) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NICTRACE); + /* Get the number of network adapters */ + ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine); + unsigned n = parseNum(&a->argv[1][8], NetworkAdapterCount, "NIC"); + if (!n) + { + hrc = E_FAIL; + break; + } + if (a->argc <= 2) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + + /* get the corresponding network adapter */ + ComPtr adapter; + CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam())); + if (adapter) + { + BOOL fEnabled; + adapter->COMGETTER(Enabled)(&fEnabled); + if (fEnabled) + { + bool fTraceEnabled; + if (RT_FAILURE(parseBool(a->argv[2], &fTraceEnabled))) + { + errorSyntax(ControlVM::tr("Invalid nictrace%lu argument '%s'."), n, a->argv[2]); + hrc = E_FAIL; + break; + } + CHECK_ERROR_RET(adapter, COMSETTER(TraceEnabled)(fTraceEnabled), RTEXITCODE_FAILURE); + fNeedsSaving = true; + } + else + RTMsgError(ControlVM::tr("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) + { + hrc = E_FAIL; + break; + } + if (a->argc <= 2) + { + errorArgument(ControlVM::tr("Missing argument to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + + /* get the corresponding network adapter */ + ComPtr adapter; + CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam())); + if (!adapter) + { + hrc = E_FAIL; + break; + } + ComPtr engine; + CHECK_ERROR(adapter, COMGETTER(NATEngine)(engine.asOutParam())); + if (!engine) + { + hrc = E_FAIL; + break; + } + + if (!strcmp(a->argv[2], "delete")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NATPF_DELETE); + if (a->argc >= 3) + CHECK_ERROR(engine, RemoveRedirect(Bstr(a->argv[3]).raw())); + } + else + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NATPF); +#define ITERATE_TO_NEXT_TERM(ch) \ + do { \ + while (*ch != ',') \ + { \ + if (*ch == 0) \ + { \ + return errorSyntax(ControlVM::tr("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(ControlVM::tr("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(hrc)) + fNeedsSaving = true; + } + else if (!strncmp(a->argv[1], "nicproperty", 11)) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NICPROPERTY); + /* Get the number of network adapters */ + ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine); + unsigned n = parseNum(&a->argv[1][11], NetworkAdapterCount, "NIC"); + if (!n) + { + hrc = E_FAIL; + break; + } + if (a->argc <= 2) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + + /* get the corresponding network adapter */ + ComPtr 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(hrc)) + fNeedsSaving = true; + } + else + { + errorSyntax(ControlVM::tr("Invalid nicproperty%d argument '%s'."), n, a->argv[2]); + hrc = E_FAIL; + } + RTStrFree(pszProperty); + } + else + { + RTMsgError(ControlVM::tr("Failed to allocate memory for nicproperty%d '%s'."), + n, a->argv[2]); + hrc = E_FAIL; + } + if (FAILED(hrc)) + break; + } + else + RTMsgError(ControlVM::tr("The NIC %d is currently disabled and thus its properties can't be changed."), n); + } + } + else if (!strncmp(a->argv[1], "nicpromisc", 10)) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NICPROMISC); + /* Get the number of network adapters */ + ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine); + unsigned n = parseNum(&a->argv[1][10], NetworkAdapterCount, "NIC"); + if (!n) + { + hrc = E_FAIL; + break; + } + if (a->argc <= 2) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + + /* get the corresponding network adapter */ + ComPtr 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 + { + errorSyntax(ControlVM::tr("Unknown promiscuous mode policy '%s'."), a->argv[2]); + hrc = E_INVALIDARG; + break; + } + + CHECK_ERROR(adapter, COMSETTER(PromiscModePolicy)(enmPromiscModePolicy)); + if (SUCCEEDED(hrc)) + fNeedsSaving = true; + } + else + RTMsgError(ControlVM::tr("The NIC %d is currently disabled and thus its promiscuous mode can't be changed."), n); + } + } + else if (!strncmp(a->argv[1], "nic", 3)) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NIC); + /* Get the number of network adapters */ + ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine); + unsigned n = parseNum(&a->argv[1][3], NetworkAdapterCount, "NIC"); + if (!n) + { + hrc = E_FAIL; + break; + } + if (a->argc <= 2) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + + /* get the corresponding network adapter */ + ComPtr 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) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[2]); + hrc = E_FAIL; + break; + } + CHECK_ERROR_RET(adapter, COMSETTER(BridgedInterface)(Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE); + verifyHostNetworkInterfaceName(a->virtualBox, a->argv[3], HostNetworkInterfaceType_Bridged); + CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Bridged), RTEXITCODE_FAILURE); + } + else if (!strcmp(a->argv[2], "intnet")) + { + if (a->argc <= 3) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[2]); + hrc = 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) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[2]); + hrc = E_FAIL; + break; + } + CHECK_ERROR_RET(adapter, COMSETTER(HostOnlyInterface)(Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE); + verifyHostNetworkInterfaceName(a->virtualBox, a->argv[3], HostNetworkInterfaceType_HostOnly); + CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_HostOnly), RTEXITCODE_FAILURE); + } +#endif + else if (!strcmp(a->argv[2], "generic")) + { + if (a->argc <= 3) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[2]); + hrc = 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) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[2]); + hrc = 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); + } + else + { + errorSyntax(ControlVM::tr("Invalid type '%s' specfied for NIC %lu."), a->argv[2], n); + hrc = E_FAIL; + break; + } + if (SUCCEEDED(hrc)) + fNeedsSaving = true; + } + else + RTMsgError(ControlVM::tr("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")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_VRDE); + if (!strcmp(a->argv[1], "vrdp")) + RTMsgWarning(ControlVM::tr("'vrdp' is deprecated. Use 'vrde'.")); + + if (a->argc <= 1 + 1) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + ComPtr vrdeServer; + sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam()); + ASSERT(vrdeServer); + if (vrdeServer) + { + bool fEnabled; + if (RT_FAILURE(parseBool(a->argv[2], &fEnabled))) + { + errorSyntax(ControlVM::tr("Invalid remote desktop server state '%s'."), a->argv[2]); + hrc = E_FAIL; + break; + } + CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(fEnabled)); + fNeedsSaving = true; + } + } + else if ( !strcmp(a->argv[1], "vrdeport") + || !strcmp(a->argv[1], "vrdpport")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_VRDEPORT); + if (!strcmp(a->argv[1], "vrdpport")) + RTMsgWarning(ControlVM::tr("'vrdpport' is deprecated. Use 'vrdeport'.")); + + if (a->argc <= 1 + 1) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + + ComPtr 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(hrc)) + fNeedsSaving = true; + } + } + else if ( !strcmp(a->argv[1], "vrdevideochannelquality") + || !strcmp(a->argv[1], "vrdpvideochannelquality")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_VRDEVIDEOCHANNELQUALITY); + if (!strcmp(a->argv[1], "vrdpvideochannelquality")) + RTMsgWarning(ControlVM::tr("'vrdpvideochannelquality' is deprecated. Use 'vrdevideochannelquality'.")); + + if (a->argc <= 1 + 1) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + ComPtr 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(hrc)) + fNeedsSaving = true; + } + } + else if (!strcmp(a->argv[1], "vrdeproperty")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_VRDEPROPERTY); + if (a->argc <= 1 + 1) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + ComPtr 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(hrc)) + fNeedsSaving = true; + } + else + { + errorSyntax(ControlVM::tr("Invalid vrdeproperty argument '%s'."), a->argv[2]); + hrc = E_FAIL; + } + RTStrFree(pszProperty); + } + else + { + RTMsgError(ControlVM::tr("Failed to allocate memory for VRDE property '%s'."), + a->argv[2]); + hrc = E_FAIL; + } + } + if (FAILED(hrc)) + { + break; + } + } + else if ( !strcmp(a->argv[1], "usbattach") + || !strcmp(a->argv[1], "usbdetach")) + { + bool attach = !strcmp(a->argv[1], "usbattach"); + if (attach) + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_USBATTACH); + else + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_USBDETACH); + + if (a->argc < 3) + { + errorSyntax(ControlVM::tr("Not enough parameters.")); + hrc = E_FAIL; + break; + } + else if (a->argc == 4 || a->argc > 5) + { + errorSyntax(ControlVM::tr("Wrong number of arguments.")); + hrc = E_FAIL; + break; + } + + Bstr usbId = a->argv[2]; + Bstr captureFilename; + + if (a->argc == 5) + { + if (!strcmp(a->argv[3], "--capturefile")) + captureFilename = a->argv[4]; + else + { + errorSyntax(ControlVM::tr("Invalid parameter '%s'."), a->argv[3]); + hrc = E_FAIL; + break; + } + } + + Guid guid(usbId); + if (!guid.isValid()) + { + // assume address + if (attach) + { + ComPtr host; + CHECK_ERROR_BREAK(a->virtualBox, COMGETTER(Host)(host.asOutParam())); + SafeIfaceArray coll; + CHECK_ERROR_BREAK(host, COMGETTER(USBDevices)(ComSafeArrayAsOutParam(coll))); + ComPtr dev; + CHECK_ERROR_BREAK(host, FindUSBDeviceByAddress(Bstr(a->argv[2]).raw(), + dev.asOutParam())); + CHECK_ERROR_BREAK(dev, COMGETTER(Id)(usbId.asOutParam())); + } + else + { + SafeIfaceArray coll; + CHECK_ERROR_BREAK(console, COMGETTER(USBDevices)(ComSafeArrayAsOutParam(coll))); + ComPtr 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()) + { + errorSyntax(ControlVM::tr("Zero UUID argument '%s'."), a->argv[2]); + hrc = E_FAIL; + break; + } + + if (attach) + CHECK_ERROR_BREAK(console, AttachUSBDevice(usbId.raw(), captureFilename.raw())); + else + { + ComPtr dev; + CHECK_ERROR_BREAK(console, DetachUSBDevice(usbId.raw(), + dev.asOutParam())); + } + } + else if (!strcmp(a->argv[1], "setvideomodehint")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_SETVIDEOMODEHINT); + if (a->argc != 5 && a->argc != 6 && a->argc != 7 && a->argc != 9) + { + errorSyntax(ControlVM::tr("Incorrect number of parameters.")); + hrc = 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) + { + if (RT_FAILURE(parseBool(a->argv[6], &fEnabled))) + { + errorSyntax(ControlVM::tr("Either \"yes\" or \"no\" is expected.")); + hrc = E_FAIL; + break; + } + } + if (a->argc == 9) + { + iOriginX = RTStrToInt32(a->argv[7]); + iOriginY = RTStrToInt32(a->argv[8]); + fChangeOrigin = true; + } + + ComPtr pDisplay; + CHECK_ERROR_BREAK(console, COMGETTER(Display)(pDisplay.asOutParam())); + if (!pDisplay) + { + RTMsgError(ControlVM::tr("Guest not running.")); + hrc = E_FAIL; + break; + } + CHECK_ERROR_BREAK(pDisplay, SetVideoModeHint(uDisplayIdx, fEnabled, + fChangeOrigin, iOriginX, iOriginY, + uXRes, uYRes, uBpp, true)); + } + else if (!strcmp(a->argv[1], "setscreenlayout")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_SETSCREENLAYOUT); + if (a->argc < 4) + { + errorSyntax(ControlVM::tr("Incorrect number of parameters.")); + hrc = E_FAIL; + break; + } + + ComPtr pDisplay; + CHECK_ERROR_BREAK(console, COMGETTER(Display)(pDisplay.asOutParam())); + if (!pDisplay) + { + RTMsgError(ControlVM::tr("Guest not running.")); + hrc = E_FAIL; + break; + } + + com::SafeIfaceArray aGuestScreenInfos; + + /* Parse " on|primary | 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(ControlVM::tr("Display status must be or .")); + hrc = 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(ControlVM::tr("Incorrect number of parameters.")); + hrc = 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 pInfo; + CHECK_ERROR_BREAK(pDisplay, CreateGuestScreenInfo(aDisplay, aStatus, aPrimary, aChangeOrigin, + aOriginX, aOriginY, aWidth, aHeight, aBitsPerPixel, + pInfo.asOutParam())); + aGuestScreenInfos.push_back(pInfo); + } + + if (FAILED(hrc)) + break; + + CHECK_ERROR_BREAK(pDisplay, SetScreenLayout(ScreenLayoutMode_Apply, ComSafeArrayAsInParam(aGuestScreenInfos))); + } + else if (!strcmp(a->argv[1], "setcredentials")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_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")) + { + errorSyntax(ControlVM::tr("Invalid parameter '%s'."), a->argv[5]); + hrc = 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(ControlVM::tr("Incorrect number of parameters.")); + hrc = 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) + { + hrc = E_FAIL; + break; + } + domain = a->argv[5]; + } + + ComPtr pGuest; + CHECK_ERROR_BREAK(console, COMGETTER(Guest)(pGuest.asOutParam())); + if (!pGuest) + { + RTMsgError(ControlVM::tr("Guest not running.")); + hrc = E_FAIL; + break; + } + CHECK_ERROR_BREAK(pGuest, SetCredentials(Bstr(a->argv[2]).raw(), + Bstr(passwd).raw(), + Bstr(domain).raw(), + fAllowLocalLogon)); + } + else if (!strcmp(a->argv[1], "guestmemoryballoon")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_GUESTMEMORYBALLOON); + if (a->argc != 3) + { + errorSyntax(ControlVM::tr("Incorrect number of parameters.")); + hrc = E_FAIL; + break; + } + uint32_t uVal; + int vrc; + vrc = RTStrToUInt32Ex(a->argv[2], NULL, 0, &uVal); + if (vrc != VINF_SUCCESS) + { + errorSyntax(ControlVM::tr("Error parsing guest memory balloon size '%s'."), a->argv[2]); + hrc = E_FAIL; + break; + } + /* guest is running; update IGuest */ + ComPtr pGuest; + hrc = console->COMGETTER(Guest)(pGuest.asOutParam()); + if (SUCCEEDED(hrc)) + { + if (!pGuest) + { + RTMsgError(ControlVM::tr("Guest not running.")); + hrc = 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 */ + { "--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); + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_TELEPORT); + int ch; + RTGETOPTUNION Value; + while ( SUCCEEDED(hrc) + && (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) + hrc = E_FAIL; + break; + } + case 'W': strPassword = Value.psz; break; + case 't': cMsTimeout = Value.u32; break; + default: + errorGetOpt(ch, &Value); + hrc = E_FAIL; + break; + } + } + if (FAILED(hrc)) + break; + + ComPtr progress; + CHECK_ERROR_BREAK(console, Teleport(bstrHostname.raw(), uPort, + Bstr(strPassword).raw(), + uMaxDowntime, + progress.asOutParam())); + + if (cMsTimeout) + { + hrc = progress->COMSETTER(Timeout)(cMsTimeout); + if (FAILED(hrc) && hrc != VBOX_E_INVALID_OBJECT_STATE) + CHECK_ERROR_BREAK(progress, COMSETTER(Timeout)(cMsTimeout)); /* lazyness */ + } + + hrc = showProgress(progress); + CHECK_PROGRESS_ERROR(progress, (ControlVM::tr("Teleportation failed"))); + } + else if (!strcmp(a->argv[1], "screenshotpng")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_SCREENSHOTPNG); + if (a->argc <= 2 || a->argc > 4) + { + errorSyntax(ControlVM::tr("Incorrect number of parameters.")); + hrc = 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) + { + errorSyntax(ControlVM::tr("Error parsing display number '%s'."), a->argv[3]); + hrc = E_FAIL; + break; + } + } + ComPtr pDisplay; + CHECK_ERROR_BREAK(console, COMGETTER(Display)(pDisplay.asOutParam())); + if (!pDisplay) + { + RTMsgError(ControlVM::tr("Guest not running.")); + hrc = 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 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(ControlVM::tr("Failed to create file '%s' (%Rrc)."), a->argv[2], vrc); + hrc = E_FAIL; + break; + } + vrc = RTFileWrite(pngFile, saScreenshot.raw(), saScreenshot.size(), NULL); + if (RT_FAILURE(vrc)) + { + RTMsgError(ControlVM::tr("Failed to write screenshot to file '%s' (%Rrc)."), a->argv[2], vrc); + hrc = E_FAIL; + } + RTFileClose(pngFile); + } +#ifdef VBOX_WITH_RECORDING + else if ( !strcmp(a->argv[1], "recording") + || !strcmp(a->argv[1], "videocap") /* legacy command */) + { + if (a->argc < 3) + { + errorSyntax(ControlVM::tr("Incorrect number of parameters.")); + hrc = E_FAIL; + break; + } + + ComPtr recordingSettings; + CHECK_ERROR_BREAK(sessionMachine, COMGETTER(RecordingSettings)(recordingSettings.asOutParam())); + + SafeIfaceArray saRecordingScreenScreens; + CHECK_ERROR_BREAK(recordingSettings, COMGETTER(Screens)(ComSafeArrayAsOutParam(saRecordingScreenScreens))); + + ComPtr pGraphicsAdapter; + CHECK_ERROR_BREAK(sessionMachine, COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam())); + + /* 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. + */ + bool fEnabled; + if (RT_SUCCESS(parseBool(a->argv[2], &fEnabled))) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING); + CHECK_ERROR_RET(recordingSettings, COMSETTER(Enabled)(fEnabled), RTEXITCODE_FAILURE); + } + else if (!strcmp(a->argv[2], "screens")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_SCREENS); + ULONG cMonitors = 64; + CHECK_ERROR_BREAK(pGraphicsAdapter, COMGETTER(MonitorCount)(&cMonitors)); + com::SafeArray saScreens(cMonitors); + if (a->argc != 4) + { + errorSyntax(ControlVM::tr("Incorrect number of parameters.")); + hrc = E_FAIL; + break; + } + if (RT_FAILURE(parseScreens(a->argv[3], &saScreens))) + { + errorSyntax(ControlVM::tr("Error parsing list of screen IDs '%s'."), a->argv[3]); + hrc = E_FAIL; + break; + } + + 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")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_FILENAME); + if (a->argc != 4) + { + errorSyntax(ControlVM::tr("Incorrect number of parameters.")); + hrc = 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")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_VIDEORES); + if (a->argc != 5) + { + errorSyntax(ControlVM::tr("Incorrect number of parameters.")); + hrc = E_FAIL; + break; + } + + uint32_t uWidth; + int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uWidth); + if (RT_FAILURE(vrc)) + { + errorSyntax(ControlVM::tr("Error parsing video width '%s'."), a->argv[3]); + hrc = E_FAIL; + break; + } + + uint32_t uHeight; + vrc = RTStrToUInt32Ex(a->argv[4], NULL, 0, &uHeight); + if (RT_FAILURE(vrc)) + { + errorSyntax(ControlVM::tr("Error parsing video height '%s'."), a->argv[4]); + hrc = 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")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_VIDEORATE); + if (a->argc != 4) + { + errorSyntax(ControlVM::tr("Incorrect number of parameters.")); + hrc = E_FAIL; + break; + } + + uint32_t uRate; + int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uRate); + if (RT_FAILURE(vrc)) + { + errorSyntax(ControlVM::tr("Error parsing video rate '%s'."), a->argv[3]); + hrc = 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")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_VIDEOFPS); + if (a->argc != 4) + { + errorSyntax(ControlVM::tr("Incorrect number of parameters.")); + hrc = E_FAIL; + break; + } + + uint32_t uFPS; + int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uFPS); + if (RT_FAILURE(vrc)) + { + errorSyntax(ControlVM::tr("Error parsing video FPS '%s'."), a->argv[3]); + hrc = 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")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_MAXTIME); + if (a->argc != 4) + { + errorSyntax(ControlVM::tr("Incorrect number of parameters.")); + hrc = E_FAIL; + break; + } + + uint32_t uMaxTime; + int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uMaxTime); + if (RT_FAILURE(vrc)) + { + errorSyntax(ControlVM::tr("Error parsing maximum time '%s'."), a->argv[3]); + hrc = 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")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_MAXFILESIZE); + if (a->argc != 4) + { + errorSyntax(ControlVM::tr("Incorrect number of parameters.")); + hrc = E_FAIL; + break; + } + + uint32_t uMaxFileSize; + int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uMaxFileSize); + if (RT_FAILURE(vrc)) + { + errorSyntax(ControlVM::tr("Error parsing maximum file size '%s'."), a->argv[3]); + hrc = 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 0 /* Add when the corresponding documentation is enabled. */ + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_OPTS); +#endif + if (a->argc != 4) + { + errorSyntax(ControlVM::tr("Incorrect number of parameters.")); + hrc = 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(ControlVM::tr("Missing argument to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + + ComPtr pEmulatedUSB; + CHECK_ERROR_BREAK(console, COMGETTER(EmulatedUSB)(pEmulatedUSB.asOutParam())); + if (!pEmulatedUSB) + { + RTMsgError(ControlVM::tr("Guest not running.")); + hrc = E_FAIL; + break; + } + + if (!strcmp(a->argv[2], "attach")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_WEBCAM_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")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_WEBCAM_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")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_WEBCAM_LIST); + com::SafeArray 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(ControlVM::tr("Invalid argument to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + } + else if (!strcmp(a->argv[1], "addencpassword")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_ADDENCPASSWORD); + if ( a->argc != 4 + && a->argc != 6) + { + errorSyntax(ControlVM::tr("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(ControlVM::tr("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, ControlVM::tr("Enter password:")); + if (rcExit == RTEXITCODE_FAILURE) + break; + } + else + { + RTEXITCODE rcExit = readPasswordFile(a->argv[3], &strPassword); + if (rcExit == RTEXITCODE_FAILURE) + { + RTMsgError(ControlVM::tr("Failed to read new password from file.")); + break; + } + } + + CHECK_ERROR_BREAK(console, AddEncryptionPassword(bstrPwId.raw(), Bstr(strPassword).raw(), fRemoveOnSuspend)); + } + else if (!strcmp(a->argv[1], "removeencpassword")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_REMOVEENCPASSWORD); + if (a->argc != 3) + { + errorSyntax(ControlVM::tr("Incorrect number of parameters.")); + break; + } + Bstr bstrPwId(a->argv[2]); + CHECK_ERROR_BREAK(console, RemoveEncryptionPassword(bstrPwId.raw())); + } + else if (!strcmp(a->argv[1], "removeallencpasswords")) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_REMOVEALLENCPASSWORDS); + CHECK_ERROR_BREAK(console, ClearAllEncryptionPasswords()); + } + else if (!strncmp(a->argv[1], "changeuartmode", 14)) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_CHANGEUARTMODE); + unsigned n = parseNum(&a->argv[1][14], 4, "UART"); + if (!n) + { + hrc = E_FAIL; + break; + } + if (a->argc < 3) + { + errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + + ComPtr uart; + + CHECK_ERROR_BREAK(sessionMachine, GetSerialPort(n - 1, uart.asOutParam())); + ASSERT(uart); + + if (!RTStrICmp(a->argv[2], "disconnected")) + { + if (a->argc != 3) + { + errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]); + hrc = 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) + { + errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]); + hrc = 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) + { + errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + CHECK_ERROR(uart, COMSETTER(Path)(Bstr(a->argv[2]).raw())); + CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostDevice)); + } + } + else if (!strncmp(a->argv[1], "vm-process-priority", 14)) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_VM_PROCESS_PRIORITY); + if (a->argc != 3) + { + errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + VMProcPriority_T enmPriority = nameToVMProcPriority(a->argv[2]); + if (enmPriority == VMProcPriority_Invalid) + { + errorSyntax(ControlVM::tr("Invalid vm-process-priority '%s'."), a->argv[2]); + hrc = E_FAIL; + } + else + { + CHECK_ERROR(sessionMachine, COMSETTER(VMProcessPriority)(enmPriority)); + } + break; + } + else if (!strncmp(a->argv[1], "autostart-enabled", 17)) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_AUTOSTART_ENABLED); + if (a->argc != 3) + { + errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + bool fEnabled; + if (RT_FAILURE(parseBool(a->argv[2], &fEnabled))) + { + errorSyntax(ControlVM::tr("Invalid value '%s'."), a->argv[2]); + hrc = E_FAIL; + break; + } + CHECK_ERROR(sessionMachine, COMSETTER(AutostartEnabled)(TRUE)); + fNeedsSaving = true; + break; + } + else if (!strncmp(a->argv[1], "autostart-delay", 15)) + { + setCurrentSubcommand(HELP_SCOPE_CONTROLVM_AUTOSTART_DELAY); + if (a->argc != 3) + { + errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]); + hrc = E_FAIL; + break; + } + uint32_t u32; + char *pszNext; + int vrc = RTStrToUInt32Ex(a->argv[2], &pszNext, 10, &u32); + if (RT_FAILURE(vrc) || *pszNext != '\0') + { + errorSyntax(ControlVM::tr("Invalid autostart delay number '%s'."), a->argv[2]); + hrc = E_FAIL; + break; + } + CHECK_ERROR(sessionMachine, COMSETTER(AutostartDelay)(u32)); + if (SUCCEEDED(hrc)) + fNeedsSaving = true; + break; + } + else + { + errorSyntax(ControlVM::tr("Invalid parameter '%s'."), a->argv[1]); + hrc = E_FAIL; + } + } while (0); + + /* The client has to trigger saving the state explicitely. */ + if (fNeedsSaving) + CHECK_ERROR(sessionMachine, SaveSettings()); + + a->session->UnlockMachine(); + + return SUCCEEDED(hrc) ? 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..c7b420ac --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageDHCPServer.cpp @@ -0,0 +1,1343 @@ +/* $Id: VBoxManageDHCPServer.cpp $ */ +/** @file + * VBoxManage - Implementation of dhcpserver command. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "VBoxManage.h" + +#include +#include + +using namespace com; + +DECLARE_TRANSLATION_CONTEXT(DHCPServer); + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define DHCPD_CMD_COMMON_OPT_NETWORK 999 /**< The --network / --netname option number. */ +#define DHCPD_CMD_COMMON_OPT_INTERFACE 998 /**< The --interface / --ifname option number. */ +/** Common option definitions. */ +#define DHCPD_CMD_COMMON_OPTION_DEFS() \ + { "--network", DHCPD_CMD_COMMON_OPT_NETWORK, RTGETOPT_REQ_STRING }, \ + { "--netname", DHCPD_CMD_COMMON_OPT_NETWORK, RTGETOPT_REQ_STRING }, /* legacy */ \ + { "--interface", DHCPD_CMD_COMMON_OPT_INTERFACE, RTGETOPT_REQ_STRING }, \ + { "--ifname", DHCPD_CMD_COMMON_OPT_INTERFACE, RTGETOPT_REQ_STRING } /* legacy */ + +/** Handles common options in the typical option parsing switch. */ +#define DHCPD_CMD_COMMON_OPTION_CASES(a_pCtx, a_ch, a_pValueUnion) \ + case DHCPD_CMD_COMMON_OPT_NETWORK: \ + if ((a_pCtx)->pszInterface != NULL) \ + return errorSyntax(DHCPServer::tr("Either --network or --interface, not both")); \ + (a_pCtx)->pszNetwork = ValueUnion.psz; \ + break; \ + case DHCPD_CMD_COMMON_OPT_INTERFACE: \ + if ((a_pCtx)->pszNetwork != NULL) \ + return errorSyntax(DHCPServer::tr("Either --interface or --network, not both")); \ + (a_pCtx)->pszInterface = ValueUnion.psz; \ + break + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** Pointer to a dhcpserver command context. */ +typedef struct DHCPDCMDCTX *PDHCPDCMDCTX; + +/** + * Definition of a dhcpserver command, with handler and various flags. + */ +typedef struct DHCPDCMDDEF +{ + /** The command name. */ + const char *pszName; + + /** + * Actual command handler callback. + * + * @param pCtx Pointer to command context to use. + */ + DECLR3CALLBACKMEMBER(RTEXITCODE, pfnHandler, (PDHCPDCMDCTX pCtx, int argc, char **argv)); + + /** The sub-command scope flags. */ + uint64_t fSubcommandScope; +} DHCPDCMDDEF; +/** Pointer to a const dhcpserver command definition. */ +typedef DHCPDCMDDEF const *PCDHCPDCMDDEF; + +/** + * dhcpserver command context (mainly for carrying common options and such). + */ +typedef struct DHCPDCMDCTX +{ + /** The handler arguments from the main() function. */ + HandlerArg *pArg; + /** Pointer to the command definition. */ + PCDHCPDCMDDEF pCmdDef; + /** The network name. */ + const char *pszNetwork; + /** The (trunk) interface name. */ + const char *pszInterface; +} DHCPDCMDCTX; + +typedef std::pair DhcpOptSpec; +typedef std::vector DhcpOpts; +typedef DhcpOpts::iterator DhcpOptIterator; + +typedef std::vector DhcpOptIds; +typedef DhcpOptIds::iterator DhcpOptIdIterator; + +struct VmNameSlotKey +{ + const Utf8Str VmName; + uint8_t u8Slot; + + VmNameSlotKey(const Utf8Str &aVmName, uint8_t aSlot) + : VmName(aVmName) + , u8Slot(aSlot) + {} + + bool operator<(const VmNameSlotKey& that) const + { + if (VmName == that.VmName) + return u8Slot < that.u8Slot; + return VmName < that.VmName; + } +}; + +typedef std::map VmSlot2OptionsM; +typedef VmSlot2OptionsM::iterator VmSlot2OptionsIterator; +typedef VmSlot2OptionsM::value_type VmSlot2OptionsPair; + +typedef std::map VmSlot2OptionIdsM; +typedef VmSlot2OptionIdsM::iterator VmSlot2OptionIdsIterator; + + + +/** + * Helper that find the DHCP server instance. + * + * @returns The DHCP server instance. NULL if failed (complaining done). + * @param pCtx The DHCP server command context. + */ +static ComPtr dhcpdFindServer(PDHCPDCMDCTX pCtx) +{ + ComPtr ptrRet; + if (pCtx->pszNetwork || pCtx->pszInterface) + { + Assert(pCtx->pszNetwork == NULL || pCtx->pszInterface == NULL); + + /* + * We need a network name to find the DHCP server. So, if interface is + * given we have to look it up. + */ + HRESULT hrc; + Bstr bstrNetName(pCtx->pszNetwork); + if (!pCtx->pszNetwork) + { + ComPtr ptrIHost; + CHECK_ERROR2_RET(hrc, pCtx->pArg->virtualBox, COMGETTER(Host)(ptrIHost.asOutParam()), ptrRet); + + Bstr bstrInterface(pCtx->pszInterface); + ComPtr ptrIHostIf; + CHECK_ERROR2(hrc, ptrIHost, FindHostNetworkInterfaceByName(bstrInterface.raw(), ptrIHostIf.asOutParam())); + if (FAILED(hrc)) + { + errorArgument(DHCPServer::tr("Failed to locate host-only interface '%s'"), pCtx->pszInterface); + return ptrRet; + } + + CHECK_ERROR2_RET(hrc, ptrIHostIf, COMGETTER(NetworkName)(bstrNetName.asOutParam()), ptrRet); + } + + /* + * Now, try locate the server + */ + hrc = pCtx->pArg->virtualBox->FindDHCPServerByNetworkName(bstrNetName.raw(), ptrRet.asOutParam()); + if (SUCCEEDED(hrc)) + return ptrRet; + if (pCtx->pszNetwork) + errorArgument(DHCPServer::tr("Failed to find DHCP server for network '%s'"), pCtx->pszNetwork); + else + errorArgument(DHCPServer::tr("Failed to find DHCP server for host-only interface '%s' (network '%ls')"), + pCtx->pszInterface, bstrNetName.raw()); + } + else + errorSyntax(DHCPServer::tr("You need to specify either --network or --interface to identify the DHCP server")); + return ptrRet; +} + + +/** + * Helper class for dhcpdHandleAddAndModify + */ +class DHCPCmdScope +{ + DHCPConfigScope_T m_enmScope; + const char *m_pszName; + uint8_t m_uSlot; + ComPtr m_ptrConfig; + ComPtr m_ptrGlobalConfig; + ComPtr m_ptrGroupConfig; + ComPtr m_ptrIndividualConfig; + +public: + DHCPCmdScope() + : m_enmScope(DHCPConfigScope_Global) + , m_pszName(NULL) + , m_uSlot(0) + { + } + + void setGlobal() + { + m_enmScope = DHCPConfigScope_Global; + m_pszName = NULL; + m_uSlot = 0; + resetPointers(); + } + + void setGroup(const char *pszGroup) + { + m_enmScope = DHCPConfigScope_Group; + m_pszName = pszGroup; + m_uSlot = 0; + resetPointers(); + } + + void setMachineNIC(const char *pszMachine) + { + m_enmScope = DHCPConfigScope_MachineNIC; + m_pszName = pszMachine; + m_uSlot = 0; + resetPointers(); + } + + void setMachineSlot(uint8_t uSlot) + { + Assert(m_enmScope == DHCPConfigScope_MachineNIC); + m_uSlot = uSlot; + resetPointers(); + } + + void setMACAddress(const char *pszMACAddress) + { + m_enmScope = DHCPConfigScope_MAC; + m_pszName = pszMACAddress; + m_uSlot = 0; + resetPointers(); + } + + ComPtr &getConfig(ComPtr const &ptrDHCPServer) + { + if (m_ptrConfig.isNull()) + { + CHECK_ERROR2I_STMT(ptrDHCPServer, GetConfig(m_enmScope, Bstr(m_pszName).raw(), m_uSlot, TRUE /*mayAdd*/, + m_ptrConfig.asOutParam()), m_ptrConfig.setNull()); + } + return m_ptrConfig; + } + + ComPtr &getIndividual(ComPtr const &ptrDHCPServer) + { + getConfig(ptrDHCPServer); + if (m_ptrIndividualConfig.isNull() && m_ptrConfig.isNotNull()) + { + HRESULT hrc = m_ptrConfig.queryInterfaceTo(m_ptrIndividualConfig.asOutParam()); + if (FAILED(hrc)) + { + com::GlueHandleComError(m_ptrConfig, "queryInterface", hrc, __FILE__, __LINE__); + m_ptrIndividualConfig.setNull(); + } + } + return m_ptrIndividualConfig; + } + + ComPtr &getGroup(ComPtr const &ptrDHCPServer) + { + getConfig(ptrDHCPServer); + if (m_ptrGroupConfig.isNull() && m_ptrConfig.isNotNull()) + { + HRESULT hrc = m_ptrConfig.queryInterfaceTo(m_ptrGroupConfig.asOutParam()); + if (FAILED(hrc)) + { + com::GlueHandleComError(m_ptrConfig, "queryInterface", hrc, __FILE__, __LINE__); + m_ptrGroupConfig.setNull(); + } + } + return m_ptrGroupConfig; + } + + DHCPConfigScope_T getScope() const { return m_enmScope; } + +private: + void resetPointers() + { + m_ptrConfig.setNull(); + m_ptrGlobalConfig.setNull(); + m_ptrIndividualConfig.setNull(); + m_ptrGroupConfig.setNull(); + } +}; + +enum +{ + DHCP_ADDMOD = 1000, + DHCP_ADDMOD_FORCE_OPTION, + DHCP_ADDMOD_UNFORCE_OPTION, + DHCP_ADDMOD_SUPPRESS_OPTION, + DHCP_ADDMOD_UNSUPPRESS_OPTION, + DHCP_ADDMOD_ZAP_OPTIONS, + DHCP_ADDMOD_INCL_MAC, + DHCP_ADDMOD_EXCL_MAC, + DHCP_ADDMOD_DEL_MAC, + DHCP_ADDMOD_INCL_MAC_WILD, + DHCP_ADDMOD_EXCL_MAC_WILD, + DHCP_ADDMOD_DEL_MAC_WILD, + DHCP_ADDMOD_INCL_VENDOR, + DHCP_ADDMOD_EXCL_VENDOR, + DHCP_ADDMOD_DEL_VENDOR, + DHCP_ADDMOD_INCL_VENDOR_WILD, + DHCP_ADDMOD_EXCL_VENDOR_WILD, + DHCP_ADDMOD_DEL_VENDOR_WILD, + DHCP_ADDMOD_INCL_USER, + DHCP_ADDMOD_EXCL_USER, + DHCP_ADDMOD_DEL_USER, + DHCP_ADDMOD_INCL_USER_WILD, + DHCP_ADDMOD_EXCL_USER_WILD, + DHCP_ADDMOD_DEL_USER_WILD, + DHCP_ADDMOD_ZAP_CONDITIONS +}; + +/** + * Handles the 'add' and 'modify' subcommands. + */ +static DECLCALLBACK(RTEXITCODE) dhcpdHandleAddAndModify(PDHCPDCMDCTX pCtx, int argc, char **argv) +{ + static const RTGETOPTDEF s_aOptions[] = + { + DHCPD_CMD_COMMON_OPTION_DEFS(), + { "--server-ip", 'a', RTGETOPT_REQ_STRING }, + { "--ip", 'a', RTGETOPT_REQ_STRING }, // deprecated + { "-ip", 'a', RTGETOPT_REQ_STRING }, // deprecated + { "--netmask", 'm', RTGETOPT_REQ_STRING }, + { "-netmask", 'm', RTGETOPT_REQ_STRING }, // deprecated + { "--lower-ip", 'l', RTGETOPT_REQ_STRING }, + { "--lowerip", 'l', RTGETOPT_REQ_STRING }, + { "-lowerip", 'l', RTGETOPT_REQ_STRING }, // deprecated + { "--upper-ip", 'u', RTGETOPT_REQ_STRING }, + { "--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 + { "--global", 'g', RTGETOPT_REQ_NOTHING }, + { "--group", 'G', RTGETOPT_REQ_STRING }, + { "--mac-address", 'E', RTGETOPT_REQ_MACADDR }, + { "--vm", 'M', RTGETOPT_REQ_STRING }, + { "--nic", 'n', RTGETOPT_REQ_UINT8 }, + { "--set-opt", 's', RTGETOPT_REQ_UINT8 }, + { "--set-opt-hex", 'x', RTGETOPT_REQ_UINT8 }, + { "--del-opt", 'D', RTGETOPT_REQ_UINT8 }, + { "--force-opt", DHCP_ADDMOD_FORCE_OPTION, RTGETOPT_REQ_UINT8 }, + { "--unforce-opt", DHCP_ADDMOD_UNFORCE_OPTION, RTGETOPT_REQ_UINT8 }, + { "--suppress-opt", DHCP_ADDMOD_SUPPRESS_OPTION, RTGETOPT_REQ_UINT8 }, + { "--unsuppress-opt", DHCP_ADDMOD_UNSUPPRESS_OPTION, RTGETOPT_REQ_UINT8 }, + { "--zap-options", DHCP_ADDMOD_ZAP_OPTIONS, RTGETOPT_REQ_NOTHING }, + { "--min-lease-time", 'q' , RTGETOPT_REQ_UINT32 }, + { "--default-lease-time", 'L' , RTGETOPT_REQ_UINT32 }, + { "--max-lease-time", 'Q' , RTGETOPT_REQ_UINT32 }, + { "--remove-config", 'R', RTGETOPT_REQ_NOTHING }, + { "--fixed-address", 'f', RTGETOPT_REQ_STRING }, + /* group conditions: */ + { "--incl-mac", DHCP_ADDMOD_INCL_MAC, RTGETOPT_REQ_STRING }, + { "--excl-mac", DHCP_ADDMOD_EXCL_MAC, RTGETOPT_REQ_STRING }, + { "--del-mac", DHCP_ADDMOD_DEL_MAC, RTGETOPT_REQ_STRING }, + { "--incl-mac-wild", DHCP_ADDMOD_INCL_MAC_WILD, RTGETOPT_REQ_STRING }, + { "--excl-mac-wild", DHCP_ADDMOD_EXCL_MAC_WILD, RTGETOPT_REQ_STRING }, + { "--del-mac-wild", DHCP_ADDMOD_DEL_MAC_WILD, RTGETOPT_REQ_STRING }, + { "--incl-vendor", DHCP_ADDMOD_INCL_VENDOR, RTGETOPT_REQ_STRING }, + { "--excl-vendor", DHCP_ADDMOD_EXCL_VENDOR, RTGETOPT_REQ_STRING }, + { "--del-vendor", DHCP_ADDMOD_DEL_VENDOR, RTGETOPT_REQ_STRING }, + { "--incl-vendor-wild", DHCP_ADDMOD_INCL_VENDOR_WILD, RTGETOPT_REQ_STRING }, + { "--excl-vendor-wild", DHCP_ADDMOD_EXCL_VENDOR_WILD, RTGETOPT_REQ_STRING }, + { "--del-vendor-wild", DHCP_ADDMOD_DEL_VENDOR_WILD, RTGETOPT_REQ_STRING }, + { "--incl-user", DHCP_ADDMOD_INCL_USER, RTGETOPT_REQ_STRING }, + { "--excl-user", DHCP_ADDMOD_EXCL_USER, RTGETOPT_REQ_STRING }, + { "--del-user", DHCP_ADDMOD_DEL_USER, RTGETOPT_REQ_STRING }, + { "--incl-user-wild", DHCP_ADDMOD_INCL_USER_WILD, RTGETOPT_REQ_STRING }, + { "--excl-user-wild", DHCP_ADDMOD_EXCL_USER_WILD, RTGETOPT_REQ_STRING }, + { "--del-user-wild", DHCP_ADDMOD_DEL_USER_WILD, RTGETOPT_REQ_STRING }, + { "--zap-conditions", DHCP_ADDMOD_ZAP_CONDITIONS, RTGETOPT_REQ_NOTHING }, + /* obsolete, to be removed: */ + { "--id", 'i', RTGETOPT_REQ_UINT8 }, // obsolete, backwards compatibility only. + { "--value", 'p', RTGETOPT_REQ_STRING }, // obsolete, backwards compatibility only. + { "--remove", 'r', RTGETOPT_REQ_NOTHING }, // obsolete, backwards compatibility only. + { "--options", 'o', RTGETOPT_REQ_NOTHING }, // obsolete legacy, ignored + + }; + + /* + * Parse the arguments in two passes: + * + * 1. Validate the command line and establish the IDHCPServer settings. + * 2. Execute the various IDHCPConfig settings changes. + * + * This is considered simpler than duplicating the command line instructions + * into elaborate structures and executing these. + */ + RTEXITCODE rcExit = RTEXITCODE_SUCCESS; + ComPtr ptrDHCPServer; + for (size_t iPass = 0; iPass < 2; iPass++) + { + const char *pszServerIp = NULL; + const char *pszNetmask = NULL; + const char *pszLowerIp = NULL; + const char *pszUpperIp = NULL; + int fEnabled = -1; + + DHCPCmdScope Scope; + char szMACAddress[32]; + + bool fNeedValueOrRemove = false; /* Only used with --id; remove in 6.1+ */ + uint8_t u8OptId = 0; /* Only used too keep --id for following --value/--remove. remove in 6.1+ */ + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + RTGETOPTUNION ValueUnion; + while ((vrc = RTGetOpt(&GetState, &ValueUnion))) + { + switch (vrc) + { + DHCPD_CMD_COMMON_OPTION_CASES(pCtx, vrc, &ValueUnion); + case 'a': // --server-ip + pszServerIp = ValueUnion.psz; + break; + case 'm': // --netmask + pszNetmask = ValueUnion.psz; + break; + case 'l': // --lower-ip + pszLowerIp = ValueUnion.psz; + break; + case 'u': // --upper-ip + pszUpperIp = ValueUnion.psz; + break; + case 'e': // --enable + fEnabled = 1; + break; + case 'd': // --disable + fEnabled = 0; + break; + + /* + * Configuration selection: + */ + case 'g': // --global Sets the option scope to 'global'. + if (fNeedValueOrRemove) + return errorSyntax(DHCPServer::tr("Incomplete option sequence preseeding '--global'")); + Scope.setGlobal(); + break; + + case 'G': // --group + if (fNeedValueOrRemove) + return errorSyntax(DHCPServer::tr("Incomplete option sequence preseeding '--group'")); + if (!*ValueUnion.psz) + return errorSyntax(DHCPServer::tr("Group name cannot be empty")); + Scope.setGroup(ValueUnion.psz); + break; + + case 'E': // --mac-address + if (fNeedValueOrRemove) + return errorSyntax(DHCPServer::tr("Incomplete option sequence preseeding '--mac-address'")); + RTStrPrintf(szMACAddress, sizeof(szMACAddress), "%RTmac", &ValueUnion.MacAddr); + Scope.setMACAddress(szMACAddress); + break; + + case 'M': // --vm Sets the option scope to ValueUnion.psz + 0. + if (fNeedValueOrRemove) + return errorSyntax(DHCPServer::tr("Incomplete option sequence preseeding '--vm'")); + Scope.setMachineNIC(ValueUnion.psz); + break; + + case 'n': // --nic Sets the option scope to pszVmName + (ValueUnion.u8 - 1). + if (Scope.getScope() != DHCPConfigScope_MachineNIC) + return errorSyntax(DHCPServer::tr("--nic option requires a --vm preceeding selecting the VM it should apply to")); + if (fNeedValueOrRemove) + return errorSyntax(DHCPServer::tr("Incomplete option sequence preseeding '--nic=%u"), ValueUnion.u8); + if (ValueUnion.u8 < 1) + return errorSyntax(DHCPServer::tr("invalid NIC number: %u"), ValueUnion.u8); + Scope.setMachineSlot(ValueUnion.u8 - 1); + break; + + /* + * Modify configuration: + */ + case 's': // --set-opt num stringvalue + { + uint8_t const idAddOpt = ValueUnion.u8; + vrc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING); + if (RT_FAILURE(vrc)) + return errorFetchValue(1, "--set-opt", vrc, &ValueUnion); + if (iPass == 1) + { + ComPtr &ptrConfig = Scope.getConfig(ptrDHCPServer); + if (ptrConfig.isNull()) + return RTEXITCODE_FAILURE; + CHECK_ERROR2I_STMT(ptrConfig, SetOption((DHCPOption_T)idAddOpt, DHCPOptionEncoding_Normal, + Bstr(ValueUnion.psz).raw()), rcExit = RTEXITCODE_FAILURE); + } + break; + } + + case 'x': // --set-opt-hex num hex-string + { + uint8_t const idAddOpt = ValueUnion.u8; + vrc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING); + if (RT_FAILURE(vrc)) + return errorFetchValue(1, "--set-opt-hex", vrc, &ValueUnion); + uint8_t abBuf[256]; + size_t cbRet; + vrc = RTStrConvertHexBytesEx(ValueUnion.psz, abBuf, sizeof(abBuf), RTSTRCONVERTHEXBYTES_F_SEP_COLON, + NULL, &cbRet); + if (RT_FAILURE(vrc)) + return errorArgument(DHCPServer::tr("Malformed hex string given to --set-opt-hex %u: %s\n"), + idAddOpt, ValueUnion.psz); + if (iPass == 1) + { + ComPtr &ptrConfig = Scope.getConfig(ptrDHCPServer); + if (ptrConfig.isNull()) + return RTEXITCODE_FAILURE; + CHECK_ERROR2I_STMT(ptrConfig, SetOption((DHCPOption_T)idAddOpt, DHCPOptionEncoding_Hex, + Bstr(ValueUnion.psz).raw()), rcExit = RTEXITCODE_FAILURE); + } + break; + } + + case 'D': // --del-opt num + if (pCtx->pCmdDef->fSubcommandScope == HELP_SCOPE_DHCPSERVER_ADD) + return errorSyntax(DHCPServer::tr("--del-opt does not apply to the 'add' subcommand")); + if (iPass == 1) + { + ComPtr &ptrConfig = Scope.getConfig(ptrDHCPServer); + if (ptrConfig.isNull()) + return RTEXITCODE_FAILURE; + CHECK_ERROR2I_STMT(ptrConfig, RemoveOption((DHCPOption_T)ValueUnion.u8), rcExit = RTEXITCODE_FAILURE); + } + break; + + case DHCP_ADDMOD_UNFORCE_OPTION: // --unforce-opt + if (pCtx->pCmdDef->fSubcommandScope == HELP_SCOPE_DHCPSERVER_ADD) + return errorSyntax(DHCPServer::tr("--unforce-opt does not apply to the 'add' subcommand")); + RT_FALL_THROUGH(); + case DHCP_ADDMOD_UNSUPPRESS_OPTION: // --unsupress-opt + if (pCtx->pCmdDef->fSubcommandScope == HELP_SCOPE_DHCPSERVER_ADD) + return errorSyntax(DHCPServer::tr("--unsuppress-opt does not apply to the 'add' subcommand")); + RT_FALL_THROUGH(); + case DHCP_ADDMOD_FORCE_OPTION: // --force-opt + case DHCP_ADDMOD_SUPPRESS_OPTION: // --suppress-opt + if (iPass == 1) + { + DHCPOption_T const enmOption = (DHCPOption_T)ValueUnion.u8; + bool const fForced = vrc == DHCP_ADDMOD_FORCE_OPTION || vrc == DHCP_ADDMOD_UNFORCE_OPTION; + + /* Get the current option list: */ + ComPtr &ptrConfig = Scope.getConfig(ptrDHCPServer); + if (ptrConfig.isNull()) + return RTEXITCODE_FAILURE; + com::SafeArray Options; + if (fForced) + CHECK_ERROR2I_STMT(ptrConfig, COMGETTER(ForcedOptions)(ComSafeArrayAsOutParam(Options)), + rcExit = RTEXITCODE_FAILURE; break); + else + CHECK_ERROR2I_STMT(ptrConfig, COMGETTER(SuppressedOptions)(ComSafeArrayAsOutParam(Options)), + rcExit = RTEXITCODE_FAILURE; break); + if (vrc == DHCP_ADDMOD_FORCE_OPTION || vrc == DHCP_ADDMOD_SUPPRESS_OPTION) + { + /* Add if not present. */ + size_t iSrc; + for (iSrc = 0; iSrc < Options.size(); iSrc++) + if (Options[iSrc] == enmOption) + break; + if (iSrc < Options.size()) + break; /* already present */ + Options.push_back(enmOption); + } + else + { + /* Remove */ + size_t iDst = 0; + for (size_t iSrc = 0; iSrc < Options.size(); iSrc++) + { + DHCPOption_T enmCurOpt = Options[iSrc]; + if (enmCurOpt != enmOption) + Options[iDst++] = enmCurOpt; + } + if (iDst == Options.size()) + break; /* Not found. */ + Options.resize(iDst); + } + + /* Update the option list: */ + if (fForced) + CHECK_ERROR2I_STMT(ptrConfig, COMSETTER(ForcedOptions)(ComSafeArrayAsInParam(Options)), + rcExit = RTEXITCODE_FAILURE); + else + CHECK_ERROR2I_STMT(ptrConfig, COMSETTER(SuppressedOptions)(ComSafeArrayAsInParam(Options)), + rcExit = RTEXITCODE_FAILURE); + } + break; + + case DHCP_ADDMOD_ZAP_OPTIONS: + if (pCtx->pCmdDef->fSubcommandScope == HELP_SCOPE_DHCPSERVER_ADD) + return errorSyntax(DHCPServer::tr("--zap-options does not apply to the 'add' subcommand")); + if (iPass == 1) + { + ComPtr &ptrConfig = Scope.getConfig(ptrDHCPServer); + if (ptrConfig.isNull()) + return RTEXITCODE_FAILURE; + CHECK_ERROR2I_STMT(ptrConfig, RemoveAllOptions(), rcExit = RTEXITCODE_FAILURE); + } + break; + + case 'q': // --min-lease-time + if (iPass == 1) + { + ComPtr &ptrConfig = Scope.getConfig(ptrDHCPServer); + if (ptrConfig.isNull()) + return RTEXITCODE_FAILURE; + CHECK_ERROR2I_STMT(ptrConfig, COMSETTER(MinLeaseTime)(ValueUnion.u32), rcExit = RTEXITCODE_FAILURE); + } + break; + + case 'L': // --default-lease-time + if (iPass == 1) + { + ComPtr &ptrConfig = Scope.getConfig(ptrDHCPServer); + if (ptrConfig.isNull()) + return RTEXITCODE_FAILURE; + CHECK_ERROR2I_STMT(ptrConfig, COMSETTER(DefaultLeaseTime)(ValueUnion.u32), rcExit = RTEXITCODE_FAILURE); + } + break; + + case 'Q': // --max-lease-time + if (iPass == 1) + { + ComPtr &ptrConfig = Scope.getConfig(ptrDHCPServer); + if (ptrConfig.isNull()) + return RTEXITCODE_FAILURE; + CHECK_ERROR2I_STMT(ptrConfig, COMSETTER(MaxLeaseTime)(ValueUnion.u32), rcExit = RTEXITCODE_FAILURE); + } + break; + + case 'R': // --remove-config + if (pCtx->pCmdDef->fSubcommandScope == HELP_SCOPE_DHCPSERVER_ADD) + return errorSyntax(DHCPServer::tr("--remove-config does not apply to the 'add' subcommand")); + if (Scope.getScope() == DHCPConfigScope_Global) + return errorSyntax(DHCPServer::tr("--remove-config cannot be applied to the global config")); + if (iPass == 1) + { + ComPtr &ptrConfig = Scope.getConfig(ptrDHCPServer); + if (ptrConfig.isNull()) + return RTEXITCODE_FAILURE; + CHECK_ERROR2I_STMT(ptrConfig, Remove(), rcExit = RTEXITCODE_FAILURE); + } + Scope.setGlobal(); + break; + + case 'f': // --fixed-address + if (Scope.getScope() != DHCPConfigScope_MachineNIC && Scope.getScope() != DHCPConfigScope_MAC) + return errorSyntax(DHCPServer::tr("--fixed-address can only be applied to a VM NIC or an MAC address")); + if (iPass == 1) + { + ComPtr &ptrIndividualConfig = Scope.getIndividual(ptrDHCPServer); + if (ptrIndividualConfig.isNull()) + return RTEXITCODE_FAILURE; + CHECK_ERROR2I_STMT(ptrIndividualConfig, COMSETTER(FixedAddress)(Bstr(ValueUnion.psz).raw()), + rcExit = RTEXITCODE_FAILURE); + } + break; + + /* + * Group conditions: + */ + case DHCP_ADDMOD_INCL_MAC: + case DHCP_ADDMOD_EXCL_MAC: + case DHCP_ADDMOD_DEL_MAC: + case DHCP_ADDMOD_INCL_MAC_WILD: + case DHCP_ADDMOD_EXCL_MAC_WILD: + case DHCP_ADDMOD_DEL_MAC_WILD: + case DHCP_ADDMOD_INCL_VENDOR: + case DHCP_ADDMOD_EXCL_VENDOR: + case DHCP_ADDMOD_DEL_VENDOR: + case DHCP_ADDMOD_INCL_VENDOR_WILD: + case DHCP_ADDMOD_EXCL_VENDOR_WILD: + case DHCP_ADDMOD_DEL_VENDOR_WILD: + case DHCP_ADDMOD_INCL_USER: + case DHCP_ADDMOD_EXCL_USER: + case DHCP_ADDMOD_DEL_USER: + case DHCP_ADDMOD_INCL_USER_WILD: + case DHCP_ADDMOD_EXCL_USER_WILD: + case DHCP_ADDMOD_DEL_USER_WILD: + { + if (Scope.getScope() != DHCPConfigScope_Group) + return errorSyntax(DHCPServer::tr("A group must be selected to perform condition alterations.")); + if (!*ValueUnion.psz) + return errorSyntax(DHCPServer::tr("Condition value cannot be empty")); /* or can it? */ + if (iPass != 1) + break; + + DHCPGroupConditionType_T enmType; + switch (vrc) + { + case DHCP_ADDMOD_INCL_MAC: case DHCP_ADDMOD_EXCL_MAC: case DHCP_ADDMOD_DEL_MAC: + enmType = DHCPGroupConditionType_MAC; + break; + case DHCP_ADDMOD_INCL_MAC_WILD: case DHCP_ADDMOD_EXCL_MAC_WILD: case DHCP_ADDMOD_DEL_MAC_WILD: + enmType = DHCPGroupConditionType_MACWildcard; + break; + case DHCP_ADDMOD_INCL_VENDOR: case DHCP_ADDMOD_EXCL_VENDOR: case DHCP_ADDMOD_DEL_VENDOR: + enmType = DHCPGroupConditionType_vendorClassID; + break; + case DHCP_ADDMOD_INCL_VENDOR_WILD: case DHCP_ADDMOD_EXCL_VENDOR_WILD: case DHCP_ADDMOD_DEL_VENDOR_WILD: + enmType = DHCPGroupConditionType_vendorClassIDWildcard; + break; + case DHCP_ADDMOD_INCL_USER: case DHCP_ADDMOD_EXCL_USER: case DHCP_ADDMOD_DEL_USER: + enmType = DHCPGroupConditionType_userClassID; + break; + case DHCP_ADDMOD_INCL_USER_WILD: case DHCP_ADDMOD_EXCL_USER_WILD: case DHCP_ADDMOD_DEL_USER_WILD: + enmType = DHCPGroupConditionType_userClassIDWildcard; + break; + default: + AssertFailedReturn(RTEXITCODE_FAILURE); + } + + int fInclusive; + switch (vrc) + { + case DHCP_ADDMOD_DEL_MAC: + case DHCP_ADDMOD_DEL_MAC_WILD: + case DHCP_ADDMOD_DEL_USER: + case DHCP_ADDMOD_DEL_USER_WILD: + case DHCP_ADDMOD_DEL_VENDOR: + case DHCP_ADDMOD_DEL_VENDOR_WILD: + fInclusive = -1; + break; + case DHCP_ADDMOD_EXCL_MAC: + case DHCP_ADDMOD_EXCL_MAC_WILD: + case DHCP_ADDMOD_EXCL_USER: + case DHCP_ADDMOD_EXCL_USER_WILD: + case DHCP_ADDMOD_EXCL_VENDOR: + case DHCP_ADDMOD_EXCL_VENDOR_WILD: + fInclusive = 0; + break; + case DHCP_ADDMOD_INCL_MAC: + case DHCP_ADDMOD_INCL_MAC_WILD: + case DHCP_ADDMOD_INCL_USER: + case DHCP_ADDMOD_INCL_USER_WILD: + case DHCP_ADDMOD_INCL_VENDOR: + case DHCP_ADDMOD_INCL_VENDOR_WILD: + fInclusive = 1; + break; + default: + AssertFailedReturn(RTEXITCODE_FAILURE); + } + + ComPtr &ptrGroupConfig = Scope.getGroup(ptrDHCPServer); + if (ptrGroupConfig.isNull()) + return RTEXITCODE_FAILURE; + if (fInclusive >= 0) + { + ComPtr ptrCondition; + CHECK_ERROR2I_STMT(ptrGroupConfig, AddCondition((BOOL)fInclusive, enmType, Bstr(ValueUnion.psz).raw(), + ptrCondition.asOutParam()), rcExit = RTEXITCODE_FAILURE); + } + else + { + com::SafeIfaceArray Conditions; + CHECK_ERROR2I_STMT(ptrGroupConfig, COMGETTER(Conditions)(ComSafeArrayAsOutParam(Conditions)), + rcExit = RTEXITCODE_FAILURE; break); + bool fFound = false; + for (size_t iCond = 0; iCond < Conditions.size(); iCond++) + { + DHCPGroupConditionType_T enmCurType = DHCPGroupConditionType_MAC; + CHECK_ERROR2I_STMT(Conditions[iCond], COMGETTER(Type)(&enmCurType), + rcExit = RTEXITCODE_FAILURE; continue); + if (enmCurType == enmType) + { + Bstr bstrValue; + CHECK_ERROR2I_STMT(Conditions[iCond], COMGETTER(Value)(bstrValue.asOutParam()), + rcExit = RTEXITCODE_FAILURE; continue); + if (RTUtf16CmpUtf8(bstrValue.raw(), ValueUnion.psz) == 0) + { + CHECK_ERROR2I_STMT(Conditions[iCond], Remove(), rcExit = RTEXITCODE_FAILURE); + fFound = true; + } + } + } + if (!fFound) + rcExit = RTMsgErrorExitFailure(DHCPServer::tr("Could not find any condition of type %d with value '%s' to delete"), + enmType, ValueUnion.psz); + } + break; + } + + case DHCP_ADDMOD_ZAP_CONDITIONS: + if (Scope.getScope() != DHCPConfigScope_Group) + return errorSyntax(DHCPServer::tr("--zap-conditions can only be with a group selected")); + if (iPass == 1) + { + ComPtr &ptrGroupConfig = Scope.getGroup(ptrDHCPServer); + if (ptrGroupConfig.isNull()) + return RTEXITCODE_FAILURE; + CHECK_ERROR2I_STMT(ptrGroupConfig, RemoveAllConditions(), rcExit = RTEXITCODE_FAILURE); + } + break; + + /* + * For backwards compatibility. Remove in 6.1 or later. + */ + + case 'o': // --options - obsolete, ignored. + break; + + case 'i': // --id + if (fNeedValueOrRemove) + return errorSyntax(DHCPServer::tr("Incomplete option sequence preseeding '--id=%u"), ValueUnion.u8); + u8OptId = ValueUnion.u8; + fNeedValueOrRemove = true; + break; + + case 'p': // --value + if (!fNeedValueOrRemove) + return errorSyntax(DHCPServer::tr("--value without --id=dhcp-opt-no")); + if (iPass == 1) + { + ComPtr &ptrConfig = Scope.getConfig(ptrDHCPServer); + if (ptrConfig.isNull()) + return RTEXITCODE_FAILURE; + CHECK_ERROR2I_STMT(ptrConfig, SetOption((DHCPOption_T)u8OptId, DHCPOptionEncoding_Normal, + Bstr(ValueUnion.psz).raw()), rcExit = RTEXITCODE_FAILURE); + } + fNeedValueOrRemove = false; + break; + + case 'r': // --remove + if (pCtx->pCmdDef->fSubcommandScope == HELP_SCOPE_DHCPSERVER_ADD) + return errorSyntax(DHCPServer::tr("--remove does not apply to the 'add' subcommand")); + if (!fNeedValueOrRemove) + return errorSyntax(DHCPServer::tr("--remove without --id=dhcp-opt-no")); + + if (iPass == 1) + { + ComPtr &ptrConfig = Scope.getConfig(ptrDHCPServer); + if (ptrConfig.isNull()) + return RTEXITCODE_FAILURE; + CHECK_ERROR2I_STMT(ptrConfig, RemoveOption((DHCPOption_T)u8OptId), rcExit = RTEXITCODE_FAILURE); + } + fNeedValueOrRemove = false; + break; + + default: + return errorGetOpt(vrc, &ValueUnion); + } + } + + if (iPass != 0) + break; + + /* + * Ensure we've got mandatory options and supply defaults + * where needed (modify case) + */ + if (!pCtx->pszNetwork && !pCtx->pszInterface) + return errorSyntax(DHCPServer::tr("You need to specify either --network or --interface to identify the DHCP server")); + + if (pCtx->pCmdDef->fSubcommandScope == HELP_SCOPE_DHCPSERVER_ADD) + { + if (!pszServerIp) + rcExit = errorSyntax(DHCPServer::tr("Missing required option: --ip")); + if (!pszNetmask) + rcExit = errorSyntax(DHCPServer::tr("Missing required option: --netmask")); + if (!pszLowerIp) + rcExit = errorSyntax(DHCPServer::tr("Missing required option: --lowerip")); + if (!pszUpperIp) + rcExit = errorSyntax(DHCPServer::tr("Missing required option: --upperip")); + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + } + + /* + * Find or create the server. + */ + HRESULT hrc; + Bstr NetName; + if (!pCtx->pszNetwork) + { + ComPtr host; + CHECK_ERROR(pCtx->pArg->virtualBox, COMGETTER(Host)(host.asOutParam())); + + ComPtr hif; + CHECK_ERROR(host, FindHostNetworkInterfaceByName(Bstr(pCtx->pszInterface).mutableRaw(), hif.asOutParam())); + if (FAILED(hrc)) + return errorArgument(DHCPServer::tr("Could not find interface '%s'"), pCtx->pszInterface); + + CHECK_ERROR(hif, COMGETTER(NetworkName) (NetName.asOutParam())); + if (FAILED(hrc)) + return errorArgument(DHCPServer::tr("Could not get network name for the interface '%s'"), pCtx->pszInterface); + } + else + { + NetName = Bstr(pCtx->pszNetwork); + } + + hrc = pCtx->pArg->virtualBox->FindDHCPServerByNetworkName(NetName.mutableRaw(), ptrDHCPServer.asOutParam()); + if (pCtx->pCmdDef->fSubcommandScope == HELP_SCOPE_DHCPSERVER_ADD) + { + if (SUCCEEDED(hrc)) + return errorArgument(DHCPServer::tr("DHCP server already exists")); + + CHECK_ERROR(pCtx->pArg->virtualBox, CreateDHCPServer(NetName.mutableRaw(), ptrDHCPServer.asOutParam())); + if (FAILED(hrc)) + return errorArgument(DHCPServer::tr("Failed to create the DHCP server")); + } + else if (FAILED(hrc)) + return errorArgument(DHCPServer::tr("DHCP server does not exist")); + + /* + * Apply IDHCPServer settings: + */ + if (pszServerIp || pszNetmask || pszLowerIp || pszUpperIp) + { + Bstr bstrServerIp(pszServerIp); + Bstr bstrNetmask(pszNetmask); + Bstr bstrLowerIp(pszLowerIp); + Bstr bstrUpperIp(pszUpperIp); + + if (!pszServerIp) + { + CHECK_ERROR2_RET(hrc, ptrDHCPServer, COMGETTER(IPAddress)(bstrServerIp.asOutParam()), RTEXITCODE_FAILURE); + } + if (!pszNetmask) + { + CHECK_ERROR2_RET(hrc, ptrDHCPServer, COMGETTER(NetworkMask)(bstrNetmask.asOutParam()), RTEXITCODE_FAILURE); + } + if (!pszLowerIp) + { + CHECK_ERROR2_RET(hrc, ptrDHCPServer, COMGETTER(LowerIP)(bstrLowerIp.asOutParam()), RTEXITCODE_FAILURE); + } + if (!pszUpperIp) + { + CHECK_ERROR2_RET(hrc, ptrDHCPServer, COMGETTER(UpperIP)(bstrUpperIp.asOutParam()), RTEXITCODE_FAILURE); + } + + CHECK_ERROR2_STMT(hrc, ptrDHCPServer, SetConfiguration(bstrServerIp.raw(), bstrNetmask.raw(), + bstrLowerIp.raw(), bstrUpperIp.raw()), + rcExit = errorArgument(DHCPServer::tr("Failed to set configuration (%ls, %ls, %ls, %ls)"), bstrServerIp.raw(), + bstrNetmask.raw(), bstrLowerIp.raw(), bstrUpperIp.raw())); + } + + if (fEnabled >= 0) + { + CHECK_ERROR2_STMT(hrc, ptrDHCPServer, COMSETTER(Enabled)((BOOL)fEnabled), rcExit = RTEXITCODE_FAILURE); + } + } + + return rcExit; +} + + +/** + * Handles the 'remove' subcommand. + */ +static DECLCALLBACK(RTEXITCODE) dhcpdHandleRemove(PDHCPDCMDCTX pCtx, int argc, char **argv) +{ + /* + * Parse the command line. + */ + static const RTGETOPTDEF s_aOptions[] = + { + DHCPD_CMD_COMMON_OPTION_DEFS(), + }; + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + RTGETOPTUNION ValueUnion; + while ((vrc = RTGetOpt(&GetState, &ValueUnion))) + { + switch (vrc) + { + DHCPD_CMD_COMMON_OPTION_CASES(pCtx, vrc, &ValueUnion); + default: + return errorGetOpt(vrc, &ValueUnion); + } + } + + /* + * Locate the server and perform the requested operation. + */ + ComPtr ptrDHCPServer = dhcpdFindServer(pCtx); + if (ptrDHCPServer.isNotNull()) + { + HRESULT hrc; + CHECK_ERROR2(hrc, pCtx->pArg->virtualBox, RemoveDHCPServer(ptrDHCPServer)); + if (SUCCEEDED(hrc)) + return RTEXITCODE_SUCCESS; + errorArgument(DHCPServer::tr("Failed to remove server")); + } + return RTEXITCODE_FAILURE; +} + + +/** + * Handles the 'start' subcommand. + */ +static DECLCALLBACK(RTEXITCODE) dhcpdHandleStart(PDHCPDCMDCTX pCtx, int argc, char **argv) +{ + /* + * Parse the command line. + */ + static const RTGETOPTDEF s_aOptions[] = + { + DHCPD_CMD_COMMON_OPTION_DEFS(), + }; + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + RTGETOPTUNION ValueUnion; + while ((vrc = RTGetOpt(&GetState, &ValueUnion))) + { + switch (vrc) + { + DHCPD_CMD_COMMON_OPTION_CASES(pCtx, vrc, &ValueUnion); + default: + return errorGetOpt(vrc, &ValueUnion); + } + } + + /* + * Locate the server. + */ + ComPtr ptrDHCPServer = dhcpdFindServer(pCtx); + if (ptrDHCPServer.isNotNull()) + { + /* + * We have to figure out the trunk name and type here, which is silly to + * leave to the API client as it's a pain to get right. But here we go... + */ + static char const s_szHostOnlyPrefix[] = "HostInterfaceNetworking-"; + bool fHostOnly = true; + Bstr strTrunkName; + if (pCtx->pszInterface) + strTrunkName = pCtx->pszInterface; + else if (RTStrStartsWith(pCtx->pszNetwork, s_szHostOnlyPrefix)) + strTrunkName = &pCtx->pszNetwork[sizeof(s_szHostOnlyPrefix) - 1]; + else + fHostOnly = false; + + Bstr strTrunkType; + if (fHostOnly) +#if defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN) + strTrunkType = "netadp"; +#else /* lazy implementations: */ + strTrunkType = "netflt"; +#endif + else + strTrunkType = "whatever"; + + HRESULT hrc = ptrDHCPServer->Start(strTrunkName.raw(), strTrunkType.raw()); + if (SUCCEEDED(hrc)) + return RTEXITCODE_SUCCESS; + errorArgument(DHCPServer::tr("Failed to start the server")); + GlueHandleComErrorNoCtx(ptrDHCPServer, hrc); + } + return RTEXITCODE_FAILURE; +} + + +/** + * Handles the 'restart' subcommand. + */ +static DECLCALLBACK(RTEXITCODE) dhcpdHandleRestart(PDHCPDCMDCTX pCtx, int argc, char **argv) +{ + /* + * Parse the command line. + */ + static const RTGETOPTDEF s_aOptions[] = + { + DHCPD_CMD_COMMON_OPTION_DEFS(), + }; + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + RTGETOPTUNION ValueUnion; + while ((vrc = RTGetOpt(&GetState, &ValueUnion))) + { + switch (vrc) + { + DHCPD_CMD_COMMON_OPTION_CASES(pCtx, vrc, &ValueUnion); + default: + return errorGetOpt(vrc, &ValueUnion); + } + } + + /* + * Locate the server and perform the requested operation. + */ + ComPtr ptrDHCPServer = dhcpdFindServer(pCtx); + if (ptrDHCPServer.isNotNull()) + { + HRESULT hrc = ptrDHCPServer->Restart(); + if (SUCCEEDED(hrc)) + return RTEXITCODE_SUCCESS; + errorArgument(DHCPServer::tr("Failed to restart the server")); + GlueHandleComErrorNoCtx(ptrDHCPServer, hrc); + } + return RTEXITCODE_FAILURE; +} + + +/** + * Handles the 'stop' subcommand. + */ +static DECLCALLBACK(RTEXITCODE) dhcpdHandleStop(PDHCPDCMDCTX pCtx, int argc, char **argv) +{ + /* + * Parse the command line. + */ + static const RTGETOPTDEF s_aOptions[] = + { + DHCPD_CMD_COMMON_OPTION_DEFS(), + }; + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + RTGETOPTUNION ValueUnion; + while ((vrc = RTGetOpt(&GetState, &ValueUnion))) + { + switch (vrc) + { + DHCPD_CMD_COMMON_OPTION_CASES(pCtx, vrc, &ValueUnion); + default: + return errorGetOpt(vrc, &ValueUnion); + } + } + + /* + * Locate the server and perform the requested operation. + */ + ComPtr ptrDHCPServer = dhcpdFindServer(pCtx); + if (ptrDHCPServer.isNotNull()) + { + HRESULT hrc = ptrDHCPServer->Stop(); + if (SUCCEEDED(hrc)) + return RTEXITCODE_SUCCESS; + errorArgument(DHCPServer::tr("Failed to stop the server")); + GlueHandleComErrorNoCtx(ptrDHCPServer, hrc); + } + return RTEXITCODE_FAILURE; +} + + +/** + * Handles the 'findlease' subcommand. + */ +static DECLCALLBACK(RTEXITCODE) dhcpdHandleFindLease(PDHCPDCMDCTX pCtx, int argc, char **argv) +{ + /* + * Parse the command line. + */ + static const RTGETOPTDEF s_aOptions[] = + { + DHCPD_CMD_COMMON_OPTION_DEFS(), + { "--mac-address", 'm', RTGETOPT_REQ_MACADDR }, + + }; + + bool fHaveMacAddress = false; + RTMAC MacAddress = { { 0, 0, 0, 0, 0, 0 } }; + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + RTGETOPTUNION ValueUnion; + while ((vrc = RTGetOpt(&GetState, &ValueUnion))) + { + switch (vrc) + { + DHCPD_CMD_COMMON_OPTION_CASES(pCtx, vrc, &ValueUnion); + + case 'm': // --mac-address + fHaveMacAddress = true; + MacAddress = ValueUnion.MacAddr; + break; + + default: + return errorGetOpt(vrc, &ValueUnion); + } + } + + if (!fHaveMacAddress) + return errorSyntax(DHCPServer::tr("You need to specify a MAC address too look for")); + + /* + * Locate the server and perform the requested operation. + */ + ComPtr ptrDHCPServer = dhcpdFindServer(pCtx); + if (ptrDHCPServer.isNull()) + return RTEXITCODE_FAILURE; + + char szMac[32]; + RTStrPrintf(szMac, sizeof(szMac), "%RTmac", &MacAddress); + Bstr bstrAddress; + Bstr bstrState; + LONG64 secIssued = 0; + LONG64 secExpire = 0; + HRESULT hrc; + CHECK_ERROR2(hrc, ptrDHCPServer, FindLeaseByMAC(Bstr(szMac).raw(), 0 /*type*/, + bstrAddress.asOutParam(), bstrState.asOutParam(), &secIssued, &secExpire)); + if (SUCCEEDED(hrc)) + { + RTTIMESPEC TimeSpec; + int64_t cSecLeftToLive = secExpire - RTTimeSpecGetSeconds(RTTimeNow(&TimeSpec)); + RTTIME Time; + char szIssued[RTTIME_STR_LEN]; + RTTimeToStringEx(RTTimeExplode(&Time, RTTimeSpecSetSeconds(&TimeSpec, secIssued)), szIssued, sizeof(szIssued), 0); + char szExpire[RTTIME_STR_LEN]; + RTTimeToStringEx(RTTimeExplode(&Time, RTTimeSpecSetSeconds(&TimeSpec, secExpire)), szExpire, sizeof(szExpire), 0); + + RTPrintf(DHCPServer::tr("IP Address: %ls\n" + "MAC Address: %RTmac\n" + "State: %ls\n" + "Issued: %s (%RU64)\n" + "Expire: %s (%RU64)\n" + "TTL: %RU64 sec, currently %RU64 sec left\n"), + bstrAddress.raw(), + &MacAddress, + bstrState.raw(), + szIssued, secIssued, + szExpire, secExpire, + secExpire >= secIssued ? secExpire - secIssued : 0, cSecLeftToLive > 0 ? cSecLeftToLive : 0); + return RTEXITCODE_SUCCESS; + } + return RTEXITCODE_FAILURE; +} + + +/** + * Handles the 'dhcpserver' command. + */ +RTEXITCODE handleDHCPServer(HandlerArg *pArg) +{ + /* + * Command definitions. + */ + static const DHCPDCMDDEF s_aCmdDefs[] = + { + { "add", dhcpdHandleAddAndModify, HELP_SCOPE_DHCPSERVER_ADD }, + { "modify", dhcpdHandleAddAndModify, HELP_SCOPE_DHCPSERVER_MODIFY }, + { "remove", dhcpdHandleRemove, HELP_SCOPE_DHCPSERVER_REMOVE }, + { "start", dhcpdHandleStart, HELP_SCOPE_DHCPSERVER_START }, + { "restart", dhcpdHandleRestart, HELP_SCOPE_DHCPSERVER_RESTART }, + { "stop", dhcpdHandleStop, HELP_SCOPE_DHCPSERVER_STOP }, + { "findlease", dhcpdHandleFindLease, HELP_SCOPE_DHCPSERVER_FINDLEASE }, + }; + + /* + * VBoxManage dhcpserver [common-options] subcommand ... + */ + DHCPDCMDCTX CmdCtx; + CmdCtx.pArg = pArg; + CmdCtx.pCmdDef = NULL; + CmdCtx.pszInterface = NULL; + CmdCtx.pszNetwork = NULL; + + static const RTGETOPTDEF s_CommonOptions[] = { DHCPD_CMD_COMMON_OPTION_DEFS() }; + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, pArg->argc, pArg->argv, s_CommonOptions, RT_ELEMENTS(s_CommonOptions), 0, + 0 /* No sorting! */); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + RTGETOPTUNION ValueUnion; + while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (vrc) + { + DHCPD_CMD_COMMON_OPTION_CASES(&CmdCtx, vrc, &ValueUnion); + + case VINF_GETOPT_NOT_OPTION: + { + 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]; + setCurrentSubcommand(s_aCmdDefs[iCmd].fSubcommandScope); + return s_aCmdDefs[iCmd].pfnHandler(&CmdCtx, pArg->argc - GetState.iNext + 1, + &pArg->argv[GetState.iNext - 1]); + } + return errorUnknownSubcommand(pszCmd); + } + + default: + return errorGetOpt(vrc, &ValueUnion); + } + } + return errorNoSubcommand(); +} diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageDebugVM.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageDebugVM.cpp new file mode 100644 index 00000000..39c0866e --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageDebugVM.cpp @@ -0,0 +1,976 @@ +/* $Id: VBoxManageDebugVM.cpp $ */ +/** @file + * VBoxManage - Implementation of the debugvm command. + */ + +/* + * Copyright (C) 2012-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "VBoxManage.h" + +DECLARE_TRANSLATION_CONTEXT(DebugVM); + + +/** + * 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 vrc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, RTGETOPTINIT_FLAGS_OPTS_FIRST); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (vrc) + { + case 'c': + idCpu = ValueUnion.u32; + break; + + case VINF_GETOPT_NOT_OPTION: + if (!RTStrICmp(ValueUnion.psz, "all")) + { + com::SafeArray aBstrNames; + com::SafeArray 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(vrc, &ValueUnion); + } + } + + if (!cRegisters) + return errorSyntax(DebugVM::tr("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 vrc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, NULL, 0, 2, RTGETOPTINIT_FLAGS_OPTS_FIRST); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (vrc) + { + 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(vrc, &ValueUnion); + } + } + + if (!pszInfo) + return errorSyntax(DebugVM::tr("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 } + }; + /* + * 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 log --debug -hex". + */ + int vrc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, + RTGETOPTINIT_FLAGS_OPTS_FIRST | RTGETOPTINIT_FLAGS_NO_STD_OPTS); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (vrc) + { + 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 vrc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, 0 /*fFlags*/); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (vrc) + { + case 'c': + if (pszCompression) + return errorSyntax(DebugVM::tr("The --compression option has already been given")); + pszCompression = ValueUnion.psz; + break; + case 'f': + if (pszFilename) + return errorSyntax(DebugVM::tr("The --filename option has already been given")); + pszFilename = ValueUnion.psz; + break; + default: + return errorGetOpt(vrc, &ValueUnion); + } + } + + if (!pszFilename) + return errorSyntax(DebugVM::tr("The --filename option is required")); + + /* + * Make the filename absolute before handing it on to the API. + */ + char szAbsFilename[RTPATH_MAX]; + vrc = RTPathAbs(pszFilename, szAbsFilename, sizeof(szAbsFilename)); + if (RT_FAILURE(vrc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, DebugVM::tr("RTPathAbs failed on '%s': %Rrc"), pszFilename, vrc); + + 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(DebugVM::tr("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(DebugVM::tr("Name: %ls\n"), bstrName.raw()); + RTPrintf(DebugVM::tr("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 vrc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, RTGETOPTINIT_FLAGS_OPTS_FIRST); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0) + switch (vrc) + { + case 'n': uMaxMessages = ValueUnion.u32; break; + default: return errorGetOpt(vrc, &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 aBstrNames; + com::SafeArray aBstrValues; + + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + static const RTGETOPTDEF s_aOptions[] = + { + { "--cpu", 'c', RTGETOPT_REQ_UINT32 }, + }; + int vrc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, RTGETOPTINIT_FLAGS_OPTS_FIRST); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (vrc) + { + case 'c': + idCpu = ValueUnion.u32; + break; + + case VINF_GETOPT_NOT_OPTION: + { + const char *pszEqual = strchr(ValueUnion.psz, '='); + if (!pszEqual) + return errorSyntax(DebugVM::tr("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(DebugVM::tr("Out of memory\n")); + return RTEXITCODE_FAILURE; + } + break; + } + + default: + return errorGetOpt(vrc, &ValueUnion); + } + } + + if (!aBstrNames.size()) + return errorSyntax(DebugVM::tr("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(DebugVM::tr("Successfully set %ls\n"), aBstrNames[0]); + } + else + { + CHECK_ERROR2I_RET(pDebugger, SetRegisters(idCpu, ComSafeArrayAsInParam(aBstrNames), ComSafeArrayAsInParam(aBstrValues)), + RTEXITCODE_FAILURE); + RTPrintf(DebugVM::tr("Successfully set %u registers\n", "", aBstrNames.size()), 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(DebugVM::tr("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(DebugVM::tr("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(DebugVM::tr("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(DebugVM::tr("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 vrc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, 0 /*fFlags*/); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (vrc) + { + 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(DebugVM::tr("The show sub-command has no idea what '%s' might be"), ValueUnion.psz); + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + break; + } + + default: + return errorGetOpt(vrc, &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 vrc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, RTGETOPTINIT_FLAGS_OPTS_FIRST); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (vrc) + { + case 'c': + idCpu = ValueUnion.u32; + break; + + default: + return errorGetOpt(vrc, &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 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(DebugVM::tr("====================== 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 vrc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, 0 /*fFlags*/); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (vrc) + { + case 'd': + fWithDescriptions = true; + break; + + case 'p': + if (pszPattern) + return errorSyntax(DebugVM::tr("Multiple --pattern options are not permitted")); + pszPattern = ValueUnion.psz; + break; + + case 'r': + fReset = true; + break; + + default: + return errorGetOpt(vrc, &ValueUnion); + } + } + + if (fReset && fWithDescriptions) + return errorSyntax(DebugVM::tr("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; +} + +/** + * Handles the guestsample sub-command. + * + * @returns Suitable exit code. + * @param pArgs The handler arguments. + * @param pDebugger Pointer to the debugger interface. + */ +static RTEXITCODE handleDebugVM_GuestSample(HandlerArg *pArgs, IMachineDebugger *pDebugger) +{ + /* + * Parse arguments. + */ + const char *pszFilename = NULL; + uint32_t cSampleIntervalUs = 1000; + uint64_t cSampleTimeUs = 1000*1000; + + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + static const RTGETOPTDEF s_aOptions[] = + { + { "--filename", 'f', RTGETOPT_REQ_STRING }, + { "--sample-interval-us", 'i', RTGETOPT_REQ_UINT32 }, + { "--sample-time-us", 't', RTGETOPT_REQ_UINT64 }, + }; + int vrc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, 0 /*fFlags*/); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (vrc) + { + case 'f': + pszFilename = ValueUnion.psz; + break; + case 'i': + cSampleIntervalUs = ValueUnion.u32; + break; + case 't': + cSampleTimeUs = ValueUnion.u64; + break; + + default: + return errorGetOpt(vrc, &ValueUnion); + } + } + + if (!pszFilename) + return errorSyntax(DebugVM::tr("The --filename is missing")); + + /* + * Execute the order. + */ + ComPtr ptrProgress; + com::Bstr bstrFilename(pszFilename); + CHECK_ERROR2I_RET(pDebugger, TakeGuestSample(bstrFilename.raw(), cSampleIntervalUs, cSampleTimeUs, ptrProgress.asOutParam()), RTEXITCODE_FAILURE); + showProgress(ptrProgress); + + 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 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 ptrConsole; + CHECK_ERROR2(hrc, pArgs->session, COMGETTER(Console)(ptrConsole.asOutParam())); + if (SUCCEEDED(hrc)) + { + if (ptrConsole.isNotNull()) + { + ComPtr 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 if (!strcmp(pszSubCmd, "guestsample")) + { + setCurrentSubcommand(HELP_SCOPE_DEBUGVM_GUESTSAMPLE); + rcExit = handleDebugVM_GuestSample(pArgs, ptrDebugger); + } + else + errorUnknownSubcommand(pszSubCmd); + } + } + else + RTMsgError(DebugVM::tr("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..1e2d8845 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp @@ -0,0 +1,2753 @@ +/* $Id: VBoxManageDisk.cpp $ */ +/** @file + * VBoxManage - The disk/medium related commands. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "VBoxManage.h" +using namespace com; + +/** Medium category. */ +typedef enum MEDIUMCATEGORY +{ + MEDIUMCATEGORY_NONE = 0, + MEDIUMCATEGORY_DISK, + MEDIUMCATEGORY_DVD, + MEDIUMCATEGORY_FLOPPY +} MEDIUMCATEGORY; + +DECLARE_TRANSLATION_CONTEXT(Disk); + +// funcs +/////////////////////////////////////////////////////////////////////////////// + + +static DECLCALLBACK(void) handleVDError(void *pvUser, int vrc, RT_SRC_POS_DECL, const char *pszFormat, va_list va) +{ + RT_NOREF(pvUser); + RTMsgErrorV(pszFormat, va); + RTMsgError(Disk::tr("Error code %Rrc at %s(%u) in function %s"), vrc, RT_SRC_POS_ARGS); +} + +static int parseMediumVariant(const char *psz, MediumVariant_T *pMediumVariant) +{ + int vrc = VINF_SUCCESS; + unsigned uMediumVariant = (unsigned)(*pMediumVariant); + while (psz && *psz && RT_SUCCESS(vrc)) + { + 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 if ( !RTStrNICmp(psz, "raw", len) + || !RTStrNICmp(psz, "rawdisk", len)) + uMediumVariant |= MediumVariant_VmdkRawDisk; + else + vrc = VERR_PARSE_ERROR; + } + if (pszComma) + psz += len + 1; + else + psz += len; + } + + if (RT_SUCCESS(vrc)) + *pMediumVariant = (MediumVariant_T)uMediumVariant; + return vrc; +} + +int parseMediumType(const char *psz, MediumType_T *penmMediumType) +{ + int vrc = 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 + vrc = VERR_PARSE_ERROR; + + if (RT_SUCCESS(vrc)) + *penmMediumType = enmMediumType; + return vrc; +} + +/** @todo move this into getopt, as getting bool values is generic */ +int parseBool(const char *psz, bool *pb) +{ + int vrc = VINF_SUCCESS; + if ( !RTStrICmp(psz, "on") + || !RTStrICmp(psz, "yes") + || !RTStrICmp(psz, "true") + || !RTStrCmp(psz, "1") + || !RTStrICmp(psz, "enable") + || !RTStrICmp(psz, "enabled")) + *pb = true; + else if ( !RTStrICmp(psz, "off") + || !RTStrICmp(psz, "no") + || !RTStrICmp(psz, "false") + || !RTStrCmp(psz, "0") + || !RTStrICmp(psz, "disable") + || !RTStrICmp(psz, "disabled")) + *pb = false; + else + vrc = VERR_PARSE_ERROR; + + return vrc; +} + +HRESULT openMedium(HandlerArg *a, const char *pszFilenameOrUuid, + DeviceType_T enmDevType, AccessMode_T enmAccessMode, + ComPtr &pMedium, bool fForceNewUuidOnOpen, + bool fSilent) +{ + HRESULT hrc; + 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(Disk::tr("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 + hrc = a->virtualBox->OpenMedium(Bstr(pszFilenameOrUuid).raw(), + enmDevType, + enmAccessMode, + fForceNewUuidOnOpen, + pMedium.asOutParam()); + + return hrc; +} + +static HRESULT createMedium(HandlerArg *a, const char *pszFormat, + const char *pszFilename, DeviceType_T enmDevType, + AccessMode_T enmAccessMode, ComPtr &pMedium) +{ + HRESULT hrc; + 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(Disk::tr("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 hrc; +} + +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 + { "--property", 'p', RTGETOPT_REQ_STRING }, + { "--property-file",'P', RTGETOPT_REQ_STRING }, +}; + +class MediumProperty +{ +public: + const char *m_pszKey; + const char *m_pszValue; /**< Can be binary too. */ + size_t m_cbValue; + char *m_pszFreeValue; + MediumProperty() : m_pszKey(NULL), m_pszValue(NULL), m_cbValue(0), m_pszFreeValue(NULL) { } + MediumProperty(MediumProperty const &a_rThat) + : m_pszKey(a_rThat.m_pszKey) + , m_pszValue(a_rThat.m_pszValue) + , m_cbValue(a_rThat.m_cbValue) + , m_pszFreeValue(NULL) + { + Assert(a_rThat.m_pszFreeValue == NULL); /* not expected here! */ + } + ~MediumProperty() + { + RTMemFree(m_pszFreeValue); + m_pszFreeValue = NULL; + } + +private: + MediumProperty &operator=(MediumProperty const &a_rThat) + { + m_pszKey = a_rThat.m_pszKey; + m_pszValue = a_rThat.m_pszValue; + m_cbValue = a_rThat.m_cbValue; + m_pszFreeValue = a_rThat.m_pszFreeValue; + if (a_rThat.m_pszFreeValue != NULL) + { + m_pszFreeValue = (char *)RTMemDup(m_pszValue, m_cbValue + 1); + if (!m_pszFreeValue) + { + RTMsgError(Disk::tr("Out of memory copying '%s'"), m_pszValue); + throw std::bad_alloc(); + } + } + return *this; + } +}; + +RTEXITCODE handleCreateMedium(HandlerArg *a) +{ + std::list lstProperties; + + HRESULT hrc; + 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(Disk::tr("Only one command can be specified: '%s'"), ValueUnion.psz); + cmd = CMD_DISK; + break; + + case 'D': // DVD + if (cmd != CMD_NONE) + return errorSyntax(Disk::tr("Only one command can be specified: '%s'"), ValueUnion.psz); + cmd = CMD_DVD; + break; + + case 'L': // floppy + if (cmd != CMD_NONE) + return errorSyntax(Disk::tr("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 'p': // --property + case 'P': // --property-file + { + /* allocate property kvp, parse, and append to end of singly linked list */ + char *pszValue = (char *)strchr(ValueUnion.psz, '='); + if (!pszValue) + return RTMsgErrorExitFailure(Disk::tr("Invalid key value pair: No '='.")); + + lstProperties.push_back(MediumProperty()); + MediumProperty &rNewProp = lstProperties.back(); + *pszValue++ = '\0'; /* Warning! Modifies argument string. */ + rNewProp.m_pszKey = ValueUnion.psz; + if (c == 'p') + { + rNewProp.m_pszValue = pszValue; + rNewProp.m_cbValue = strlen(pszValue); + } + else // 'P' + { + RTFILE hValueFile = NIL_RTFILE; + vrc = RTFileOpen(&hValueFile, pszValue, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE); + if (RT_FAILURE(vrc)) + return RTMsgErrorExitFailure(Disk::tr("Cannot open replacement value file '%s': %Rrc"), pszValue, vrc); + + uint64_t cbValue = 0; + vrc = RTFileQuerySize(hValueFile, &cbValue); + if (RT_SUCCESS(vrc)) + { + if (cbValue <= _16M) + { + rNewProp.m_cbValue = (size_t)cbValue; + rNewProp.m_pszValue = rNewProp.m_pszFreeValue = (char *)RTMemAlloc(rNewProp.m_cbValue + 1); + if (rNewProp.m_pszFreeValue) + { + vrc = RTFileReadAt(hValueFile, 0, rNewProp.m_pszFreeValue, cbValue, NULL); + if (RT_SUCCESS(vrc)) + rNewProp.m_pszFreeValue[rNewProp.m_cbValue] = '\0'; + else + RTMsgError(Disk::tr("Error reading replacement MBR file '%s': %Rrc"), pszValue, vrc); + } + else + vrc = RTMsgErrorRc(VERR_NO_MEMORY, Disk::tr("Out of memory reading '%s': %Rrc"), pszValue, vrc); + } + else + vrc = RTMsgErrorRc(VERR_OUT_OF_RANGE, + Disk::tr("Replacement value file '%s' is to big: %Rhcb, max 16MiB"), + pszValue, cbValue); + } + else + RTMsgError(Disk::tr("Cannot get the size of the value file '%s': %Rrc"), pszValue, vrc); + RTFileClose(hValueFile); + if (RT_FAILURE(vrc)) + return RTEXITCODE_FAILURE; + } + 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(Disk::tr("Invalid medium variant '%s'"), ValueUnion.psz); + break; + + case VINF_GETOPT_NOT_OPTION: + return errorSyntax(Disk::tr("Invalid parameter '%s'"), ValueUnion.psz); + + default: + if (c > 0) + { + if (RT_C_IS_PRINT(c)) + return errorSyntax(Disk::tr("Invalid option -%c"), c); + else + return errorSyntax(Disk::tr("Invalid option case %i"), c); + } + else if (c == VERR_GETOPT_UNKNOWN_OPTION) + return errorSyntax(Disk::tr("unknown option: %s\n"), ValueUnion.psz); + else if (ValueUnion.pDef) + return errorSyntax("%s: %Rrs", ValueUnion.pDef->pszLong, c); + else + return errorSyntax(Disk::tr("error: %Rrs"), c); + } + } + + /* check the outcome */ + if (cmd == CMD_NONE) + cmd = CMD_DISK; + ComPtr pParentMedium; + if (fBase) + { + if (!filename || !*filename) + return errorSyntax(Disk::tr("Parameter --filename is required")); + if ((enmMediumVariant & MediumVariant_VmdkRawDisk) == 0 && size == 0) + return errorSyntax(Disk::tr("Parameter --size is 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; + } + } + if ((enmMediumVariant & MediumVariant_VmdkRawDisk) && strcmp(format, "VMDK")) + return errorSyntax(Disk::tr("Variant 'Rawdisk' requires '--format=VMDK'")); + } + else + { + if ( !filename + || !*filename) + return errorSyntax(Disk::tr("Parameter --filename is required")); + size = 0; + if (cmd != CMD_DISK) + return errorSyntax(Disk::tr("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; + } + hrc = openMedium(a, diffparent, DeviceType_HardDisk, + AccessMode_ReadWrite, pParentMedium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + if (pParentMedium.isNull()) + return RTMsgErrorExit(RTEXITCODE_FAILURE, Disk::tr("Invalid parent hard disk reference, avoiding crash")); + MediumState_T state; + CHECK_ERROR(pParentMedium, COMGETTER(State)(&state)); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + if (state == MediumState_Inaccessible) + { + CHECK_ERROR(pParentMedium, RefreshState(&state)); + if (FAILED(hrc)) + 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 pMedium; + if (cmd == CMD_DISK) + hrc = createMedium(a, format, filename, DeviceType_HardDisk, + AccessMode_ReadWrite, pMedium); + else if (cmd == CMD_DVD) + hrc = createMedium(a, format, filename, DeviceType_DVD, + AccessMode_ReadOnly, pMedium); + else if (cmd == CMD_FLOPPY) + hrc = createMedium(a, format, filename, DeviceType_Floppy, + AccessMode_ReadWrite, pMedium); + else + hrc = E_INVALIDARG; /* cannot happen but make gcc happy */ + + + if (SUCCEEDED(hrc) && pMedium) + { + if (lstProperties.size() > 0) + { + ComPtr pMediumFormat; + CHECK_ERROR2I_RET(pMedium, COMGETTER(MediumFormat)(pMediumFormat.asOutParam()), RTEXITCODE_FAILURE); + com::SafeArray propertyNames; + com::SafeArray propertyDescriptions; + com::SafeArray propertyTypes; + com::SafeArray propertyFlags; + com::SafeArray propertyDefaults; + CHECK_ERROR2I_RET(pMediumFormat, + DescribeProperties(ComSafeArrayAsOutParam(propertyNames), + ComSafeArrayAsOutParam(propertyDescriptions), + ComSafeArrayAsOutParam(propertyTypes), + ComSafeArrayAsOutParam(propertyFlags), + ComSafeArrayAsOutParam(propertyDefaults)), + RTEXITCODE_FAILURE); + + for (std::list::iterator it = lstProperties.begin(); + it != lstProperties.end(); + ++it) + { + const char * const pszKey = it->m_pszKey; + bool fBinary = true; + bool fPropertyFound = false; + for (size_t i = 0; i < propertyNames.size(); ++i) + if (RTUtf16CmpUtf8(propertyNames[i], pszKey) == 0) + { + fBinary = propertyTypes[i] == DataType_Int8; + fPropertyFound = true; + break; + } + if (!fPropertyFound) + return RTMsgErrorExit(RTEXITCODE_FAILURE, + Disk::tr("Property '%s' was not found in the list of medium properties for the requested medium format (%s)."), + pszKey, format); + if (!fBinary) + CHECK_ERROR2I_RET(pMedium, SetProperty(Bstr(pszKey).raw(), Bstr(it->m_pszValue).raw()), + RTEXITCODE_FAILURE); + else + { + com::Bstr bstrBase64Value; + hrc = bstrBase64Value.base64Encode(it->m_pszValue, it->m_cbValue); + if (FAILED(hrc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, Disk::tr("Base64 encoding of the property %s failed. (%Rhrc)"), + pszKey, hrc); + CHECK_ERROR2I_RET(pMedium, SetProperty(Bstr(pszKey).raw(), bstrBase64Value.raw()), RTEXITCODE_FAILURE); + } + } + } + + ComPtr pProgress; + com::SafeArray l_variants(sizeof(MediumVariant_T)*8); + + for (ULONG i = 0; i < l_variants.size(); ++i) + { + ULONG temp = enmMediumVariant; + temp &= 1< pMedium; + MediumType_T enmMediumType = MediumType_Normal; /* Shut up MSC */ + bool AutoReset = false; + SafeArray mediumPropNames; + SafeArray 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(Disk::tr("Only one command can be specified: '%s'"), ValueUnion.psz); + cmd = CMD_DISK; + break; + + case 'D': // DVD + if (cmd != CMD_NONE) + return errorSyntax(Disk::tr("Only one command can be specified: '%s'"), ValueUnion.psz); + cmd = CMD_DVD; + break; + + case 'L': // floppy + if (cmd != CMD_NONE) + return errorSyntax(Disk::tr("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(Disk::tr("Invalid medium type '%s'"), ValueUnion.psz); + fModifyMediumType = true; + break; + + case 'z': // --autoreset + vrc = parseBool(ValueUnion.psz, &AutoReset); + if (RT_FAILURE(vrc)) + return errorArgument(Disk::tr("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(Disk::tr("Invalid --property argument '%s'"), ValueUnion.psz); + hrc = E_FAIL; + } + RTStrFree(pszProperty); + } + else + { + RTStrmPrintf(g_pStdErr, Disk::tr("Error: Failed to allocate memory for medium property '%s'\n"), + ValueUnion.psz); + hrc = 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(Disk::tr("Invalid parameter '%s'"), ValueUnion.psz); + break; + + default: + if (c > 0) + { + if (RT_C_IS_PRINT(c)) + return errorSyntax(Disk::tr("Invalid option -%c"), c); + else + return errorSyntax(Disk::tr("Invalid option case %i"), c); + } + else if (c == VERR_GETOPT_UNKNOWN_OPTION) + return errorSyntax(Disk::tr("unknown option: %s\n"), ValueUnion.psz); + else if (ValueUnion.pDef) + return errorSyntax("%s: %Rrs", ValueUnion.pDef->pszLong, c); + else + return errorSyntax(Disk::tr("error: %Rrs"), c); + } + } + + if (cmd == CMD_NONE) + cmd = CMD_DISK; + + if (!pszFilenameOrUuid) + return errorSyntax(Disk::tr("Medium name or UUID required")); + + if (!fModifyMediumType + && !fModifyAutoReset + && !fModifyProperties + && !fModifyCompact + && !fModifyResize + && !fMoveMedium + && !fSetNewLocation + && !fModifyDescription + ) + return errorSyntax(Disk::tr("No operation specified")); + + /* Always open the medium if necessary, there is no other way. */ + if (cmd == CMD_DISK) + hrc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk, + AccessMode_ReadWrite, pMedium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + else if (cmd == CMD_DVD) + hrc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD, + AccessMode_ReadOnly, pMedium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + else if (cmd == CMD_FLOPPY) + hrc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy, + AccessMode_ReadWrite, pMedium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + else + hrc = E_INVALIDARG; /* cannot happen but make gcc happy */ + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + if (pMedium.isNull()) + { + RTMsgError(Disk::tr("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(Disk::tr("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 pProgress; + CHECK_ERROR(pMedium, Compact(pProgress.asOutParam())); + if (SUCCEEDED(hrc)) + hrc = showProgress(pProgress); + if (FAILED(hrc)) + { + if (hrc == E_NOTIMPL) + RTMsgError(Disk::tr("Compact medium operation is not implemented!")); + else if (hrc == VBOX_E_NOT_SUPPORTED) + RTMsgError(Disk::tr("Compact medium operation for this format is not implemented yet!")); + else if (!pProgress.isNull()) + CHECK_PROGRESS_ERROR(pProgress, (Disk::tr("Failed to compact medium"))); + else + RTMsgError(Disk::tr("Failed to compact medium!")); + } + } + + if (fModifyResize) + { + ComPtr pProgress; + CHECK_ERROR(pMedium, Resize(cbResize, pProgress.asOutParam())); + if (SUCCEEDED(hrc)) + hrc = showProgress(pProgress); + if (FAILED(hrc)) + { + if (!pProgress.isNull()) + CHECK_PROGRESS_ERROR(pProgress, (Disk::tr("Failed to resize medium"))); + else if (hrc == E_NOTIMPL) + RTMsgError(Disk::tr("Resize medium operation is not implemented!")); + else if (hrc == VBOX_E_NOT_SUPPORTED) + RTMsgError(Disk::tr("Resize medium operation for this format is not implemented yet!")); + else + RTMsgError(Disk::tr("Failed to resize medium!")); + } + } + + if (fMoveMedium) + { + do + { + ComPtr pProgress; + Utf8Str strLocation(pszNewLocation); + RTStrFree(pszNewLocation); + CHECK_ERROR(pMedium, MoveTo(Bstr(strLocation).raw(), pProgress.asOutParam())); + + if (SUCCEEDED(hrc) && !pProgress.isNull()) + { + hrc = showProgress(pProgress); + CHECK_PROGRESS_ERROR(pProgress, (Disk::tr("Failed to move medium"))); + } + + Bstr uuid; + CHECK_ERROR_BREAK(pMedium, COMGETTER(Id)(uuid.asOutParam())); + + RTPrintf(Disk::tr("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(Disk::tr("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(Disk::tr("Medium description has been changed.\n")); + } + + return SUCCEEDED(hrc) ? 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 }, + { "--resize", 'r', RTGETOPT_REQ_UINT64 }, +}; + +RTEXITCODE handleCloneMedium(HandlerArg *a) +{ + HRESULT hrc; + 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; + bool fNeedResize = false; + uint64_t cbResize = 0; + + 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(Disk::tr("Only one command can be specified: '%s'"), ValueUnion.psz); + cmd = CMD_DISK; + break; + + case 'D': // DVD + if (cmd != CMD_NONE) + return errorSyntax(Disk::tr("Only one command can be specified: '%s'"), ValueUnion.psz); + cmd = CMD_DVD; + break; + + case 'f': // floppy + if (cmd != CMD_NONE) + return errorSyntax(Disk::tr("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(Disk::tr("Invalid medium variant '%s'"), ValueUnion.psz); + break; + + case 'r': // --resize + fNeedResize = true; + cbResize = ValueUnion.u64 * _1M; + break; + + case VINF_GETOPT_NOT_OPTION: + if (!pszSrc) + pszSrc = ValueUnion.psz; + else if (!pszDst) + pszDst = ValueUnion.psz; + else + return errorSyntax(Disk::tr("Invalid parameter '%s'"), ValueUnion.psz); + break; + + default: + if (c > 0) + { + if (RT_C_IS_GRAPH(c)) + return errorSyntax(Disk::tr("unhandled option: -%c"), c); + else + return errorSyntax(Disk::tr("unhandled option: %i"), c); + } + else if (c == VERR_GETOPT_UNKNOWN_OPTION) + return errorSyntax(Disk::tr("unknown option: %s"), ValueUnion.psz); + else if (ValueUnion.pDef) + return errorSyntax("%s: %Rrs", ValueUnion.pDef->pszLong, c); + else + return errorSyntax(Disk::tr("error: %Rrs"), c); + } + } + + if (cmd == CMD_NONE) + cmd = CMD_DISK; + if (!pszSrc) + return errorSyntax(Disk::tr("Mandatory UUID or input file parameter missing")); + if (!pszDst) + return errorSyntax(Disk::tr("Mandatory output file parameter missing")); + if (fExisting && (!format.isEmpty() || enmMediumVariant != MediumVariant_Standard)) + return errorSyntax(Disk::tr("Specified options which cannot be used with --existing")); + + ComPtr pSrcMedium; + ComPtr pDstMedium; + + if (cmd == CMD_DISK) + hrc = openMedium(a, pszSrc, DeviceType_HardDisk, AccessMode_ReadOnly, pSrcMedium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + else if (cmd == CMD_DVD) + hrc = openMedium(a, pszSrc, DeviceType_DVD, AccessMode_ReadOnly, pSrcMedium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + else if (cmd == CMD_FLOPPY) + hrc = openMedium(a, pszSrc, DeviceType_Floppy, AccessMode_ReadOnly, pSrcMedium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + else + hrc = E_INVALIDARG; /* cannot happen but make gcc happy */ + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + do + { + /* open/create destination medium */ + if (fExisting) + { + if (cmd == CMD_DISK) + hrc = openMedium(a, pszDst, DeviceType_HardDisk, AccessMode_ReadWrite, pDstMedium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + else if (cmd == CMD_DVD) + hrc = openMedium(a, pszDst, DeviceType_DVD, AccessMode_ReadOnly, pDstMedium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + else if (cmd == CMD_FLOPPY) + hrc = openMedium(a, pszDst, DeviceType_Floppy, AccessMode_ReadWrite, pDstMedium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + if (FAILED(hrc)) + 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 pMediumFmt; + com::SafeArray 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) + hrc = createMedium(a, strFormat.c_str(), pszDst, DeviceType_HardDisk, + AccessMode_ReadWrite, pDstMedium); + else if (cmd == CMD_DVD) + hrc = createMedium(a, strFormat.c_str(), pszDst, DeviceType_DVD, + AccessMode_ReadOnly, pDstMedium); + else if (cmd == CMD_FLOPPY) + hrc = createMedium(a, strFormat.c_str(), pszDst, DeviceType_Floppy, + AccessMode_ReadWrite, pDstMedium); + if (FAILED(hrc)) + break; + } + + ComPtr pProgress; + com::SafeArray l_variants(sizeof(MediumVariant_T)*8); + + for (ULONG i = 0; i < l_variants.size(); ++i) + { + ULONG temp = enmMediumVariant; + temp &= 1<argc, a->argv, + g_aConvertFromRawHardDiskOptions, RT_ELEMENTS(g_aConvertFromRawHardDiskOptions), + 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + while ((c = RTGetOpt(&GetState, &ValueUnion))) + { + switch (c) + { + case 'u': // --uuid + if (RT_FAILURE(RTUuidFromStr(&uuid, ValueUnion.psz))) + return errorSyntax(Disk::tr("Invalid UUID '%s'"), ValueUnion.psz); + pUuid = &uuid; + break; + case 'o': // --format + format = ValueUnion.psz; + break; + + case 'm': // --variant + { + MediumVariant_T enmMediumVariant = MediumVariant_Standard; + vrc = parseMediumVariant(ValueUnion.psz, &enmMediumVariant); + if (RT_FAILURE(vrc)) + return errorArgument(Disk::tr("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(Disk::tr("Invalid parameter '%s'"), ValueUnion.psz); + break; + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + if (!srcfilename || !dstfilename || (fReadFromStdIn && !filesize)) + return errorSyntax(Disk::tr("Incorrect number of parameters")); + RTStrmPrintf(g_pStdErr, Disk::tr("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; + + vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR, + NULL, sizeof(VDINTERFACEERROR), &pVDIfs); + AssertRC(vrc); + + /* open raw image file. */ + RTFILE File; + if (fReadFromStdIn) + vrc = RTFileFromNative(&File, RTFILE_NATIVE_STDIN); + else + vrc = RTFileOpen(&File, srcfilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE); + if (RT_FAILURE(vrc)) + { + RTMsgError(Disk::tr("Cannot open file \"%s\": %Rrc"), srcfilename, vrc); + goto out; + } + + uint64_t cbFile; + /* get image size. */ + if (fReadFromStdIn) + cbFile = RTStrToUInt64(filesize); + else + vrc = RTFileQuerySize(File, &cbFile); + if (RT_FAILURE(vrc)) + { + RTMsgError(Disk::tr("Cannot get image size for file \"%s\": %Rrc"), srcfilename, vrc); + goto out; + } + + RTStrmPrintf(g_pStdErr, Disk::tr("Creating %s image with size %RU64 bytes (%RU64MB)...\n", "", cbFile), + (uImageFlags & VD_IMAGE_FLAGS_FIXED) ? Disk::tr("fixed", "adjective") : Disk::tr("dynamic", "adjective"), + cbFile, (cbFile + _1M - 1) / _1M); + char pszComment[256]; + RTStrPrintf(pszComment, sizeof(pszComment), Disk::tr("Converted image from %s"), srcfilename); + vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk); + if (RT_FAILURE(vrc)) + { + RTMsgError(Disk::tr("Cannot create the virtual disk container: %Rrc"), vrc); + 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; + vrc = VDCreateBase(pDisk, format, dstfilename, cbFile, + uImageFlags, pszComment, &PCHS, &LCHS, pUuid, + VD_OPEN_FLAGS_NORMAL, NULL, NULL); + if (RT_FAILURE(vrc)) + { + RTMsgError(Disk::tr("Cannot create the disk image \"%s\": %Rrc"), dstfilename, vrc); + goto out; + } + + size_t cbBuffer; + cbBuffer = _1M; + pvBuf = RTMemAlloc(cbBuffer); + if (!pvBuf) + { + vrc = VERR_NO_MEMORY; + RTMsgError(Disk::tr("Out of memory allocating buffers for image \"%s\": %Rrc"), dstfilename, vrc); + 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); + vrc = RTFileRead(File, pvBuf, cbToRead, &cbRead); + if (RT_FAILURE(vrc) || !cbRead) + break; + vrc = VDWrite(pDisk, offFile, pvBuf, cbRead); + if (RT_FAILURE(vrc)) + { + RTMsgError(Disk::tr("Failed to write to disk image \"%s\": %Rrc"), dstfilename, vrc); + goto out; + } + offFile += cbRead; + } + +out: + if (pvBuf) + RTMemFree(pvBuf); + if (pDisk) + VDClose(pDisk, RT_FAILURE(vrc)); + if (File != NIL_RTFILE) + RTFileClose(File); + + return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +HRESULT showMediumInfo(const ComPtr &pVirtualBox, + const ComPtr &pMedium, + const char *pszParentUUID, + bool fOptLong) +{ + HRESULT hrc = S_OK; + do + { + Bstr uuid; + pMedium->COMGETTER(Id)(uuid.asOutParam()); + RTPrintf("UUID: %ls\n", uuid.raw()); + if (pszParentUUID) + RTPrintf(Disk::tr("Parent UUID: %s\n"), pszParentUUID); + + /* check for accessibility */ + MediumState_T enmState; + CHECK_ERROR_BREAK(pMedium, RefreshState(&enmState)); + const char *pszState = Disk::tr("unknown"); + switch (enmState) + { + case MediumState_NotCreated: + pszState = Disk::tr("not created"); + break; + case MediumState_Created: + pszState = Disk::tr("created"); + break; + case MediumState_LockedRead: + pszState = Disk::tr("locked read"); + break; + case MediumState_LockedWrite: + pszState = Disk::tr("locked write"); + break; + case MediumState_Inaccessible: + pszState = Disk::tr("inaccessible"); + break; + case MediumState_Creating: + pszState = Disk::tr("creating"); + break; + case MediumState_Deleting: + pszState = Disk::tr("deleting"); + break; +#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK + case MediumState_32BitHack: break; /* Shut up compiler warnings. */ +#endif + } + RTPrintf(Disk::tr("State: %s\n"), pszState); + + if (fOptLong && enmState == MediumState_Inaccessible) + { + Bstr err; + CHECK_ERROR_BREAK(pMedium, COMGETTER(LastAccessError)(err.asOutParam())); + RTPrintf(Disk::tr("Access Error: %ls\n"), err.raw()); + } + + if (fOptLong) + { + Bstr description; + pMedium->COMGETTER(Description)(description.asOutParam()); + if (!description.isEmpty()) + RTPrintf(Disk::tr("Description: %ls\n"), description.raw()); + } + + MediumType_T type; + pMedium->COMGETTER(Type)(&type); + const char *typeStr = Disk::tr("unknown"); + switch (type) + { + case MediumType_Normal: + if (pszParentUUID && Guid(pszParentUUID).isValid()) + typeStr = Disk::tr("normal (differencing)"); + else + typeStr = Disk::tr("normal (base)"); + break; + case MediumType_Immutable: + typeStr = Disk::tr("immutable"); + break; + case MediumType_Writethrough: + typeStr = Disk::tr("writethrough"); + break; + case MediumType_Shareable: + typeStr = Disk::tr("shareable"); + break; + case MediumType_Readonly: + typeStr = Disk::tr("readonly"); + break; + case MediumType_MultiAttach: + typeStr = Disk::tr("multiattach"); + break; +#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK + case MediumType_32BitHack: break; /* Shut up compiler warnings. */ +#endif + } + RTPrintf(Disk::tr("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(Disk::tr("Auto-Reset: %s\n"), autoReset ? Disk::tr("on") : Disk::tr("off")); + } + + Bstr loc; + pMedium->COMGETTER(Location)(loc.asOutParam()); + RTPrintf(Disk::tr("Location: %ls\n"), loc.raw()); + + Bstr format; + pMedium->COMGETTER(Format)(format.asOutParam()); + RTPrintf(Disk::tr("Storage format: %ls\n"), format.raw()); + + if (fOptLong) + { + com::SafeArray 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 = Disk::tr("unknown"); + switch (variant & ~(MediumVariant_Fixed | MediumVariant_Diff)) + { + case MediumVariant_VmdkSplit2G: + variantStr = Disk::tr("split2G"); + break; + case MediumVariant_VmdkStreamOptimized: + variantStr = Disk::tr("streamOptimized"); + break; + case MediumVariant_VmdkESX: + variantStr = Disk::tr("ESX"); + break; + case MediumVariant_Standard: + variantStr = Disk::tr("default"); + break; + } + const char *variantTypeStr = Disk::tr("dynamic"); + if (variant & MediumVariant_Fixed) + variantTypeStr = Disk::tr("fixed"); + else if (variant & MediumVariant_Diff) + variantTypeStr = Disk::tr("differencing"); + RTPrintf(Disk::tr("Format variant: %s %s\n"), variantTypeStr, variantStr); + } + + LONG64 logicalSize; + pMedium->COMGETTER(LogicalSize)(&logicalSize); + RTPrintf(Disk::tr("Capacity: %lld MBytes\n"), logicalSize >> 20); + if (fOptLong) + { + LONG64 actualSize; + pMedium->COMGETTER(Size)(&actualSize); + RTPrintf(Disk::tr("Size on disk: %lld MBytes\n"), actualSize >> 20); + } + + Bstr strCipher; + Bstr strPasswordId; + HRESULT hrc2 = pMedium->GetEncryptionSettings(strCipher.asOutParam(), strPasswordId.asOutParam()); + if (SUCCEEDED(hrc2)) + { + RTPrintf(Disk::tr("Encryption: enabled\n")); + if (fOptLong) + { + RTPrintf(Disk::tr("Cipher: %ls\n"), strCipher.raw()); + RTPrintf(Disk::tr("Password ID: %ls\n"), strPasswordId.raw()); + } + } + else + RTPrintf(Disk::tr("Encryption: disabled\n")); + + if (fOptLong) + { + com::SafeArray names; + com::SafeArray 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 ? Disk::tr("Property: ") : " ", + names[i], value.raw()); + fFirst = false; + } + } + + if (fOptLong) + { + bool fFirst = true; + com::SafeArray machineIds; + pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(machineIds)); + for (size_t i = 0; i < machineIds.size(); i++) + { + ComPtr 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 ? Disk::tr("In use by VMs: ") : " ", + name.raw(), machineIds[i]); + fFirst = false; + com::SafeArray snapshotIds; + pMedium->GetSnapshotIds(machineIds[i], + ComSafeArrayAsOutParam(snapshotIds)); + for (size_t j = 0; j < snapshotIds.size(); j++) + { + ComPtr 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 children; + pMedium->COMGETTER(Children)(ComSafeArrayAsOutParam(children)); + bool fFirst = true; + for (size_t i = 0; i < children.size(); i++) + { + ComPtr pChild(children[i]); + if (pChild) + { + Bstr childUUID; + pChild->COMGETTER(Id)(childUUID.asOutParam()); + RTPrintf("%s%ls\n", + fFirst ? Disk::tr("Child UUIDs: ") : " ", + childUUID.raw()); + fFirst = false; + } + } + } + } + while (0); + + return hrc; +} + +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(Disk::tr("Only one command can be specified: '%s'"), ValueUnion.psz); + cmd = CMD_DISK; + break; + + case 'D': // DVD + if (cmd != CMD_NONE) + return errorSyntax(Disk::tr("Only one command can be specified: '%s'"), ValueUnion.psz); + cmd = CMD_DVD; + break; + + case 'f': // floppy + if (cmd != CMD_NONE) + return errorSyntax(Disk::tr("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(Disk::tr("Invalid parameter '%s'"), ValueUnion.psz); + break; + + default: + if (c > 0) + { + if (RT_C_IS_PRINT(c)) + return errorSyntax(Disk::tr("Invalid option -%c"), c); + else + return errorSyntax(Disk::tr("Invalid option case %i"), c); + } + else if (c == VERR_GETOPT_UNKNOWN_OPTION) + return errorSyntax(Disk::tr("unknown option: %s\n"), ValueUnion.psz); + else if (ValueUnion.pDef) + return errorSyntax("%s: %Rrs", ValueUnion.pDef->pszLong, c); + else + return errorSyntax(Disk::tr("error: %Rrs"), c); + } + } + + if (cmd == CMD_NONE) + cmd = CMD_DISK; + + /* check for required options */ + if (!pszFilenameOrUuid) + return errorSyntax(Disk::tr("Medium name or UUID required")); + + HRESULT hrc = S_OK; /* Prevents warning. */ + + ComPtr pMedium; + if (cmd == CMD_DISK) + hrc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk, + AccessMode_ReadOnly, pMedium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + else if (cmd == CMD_DVD) + hrc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD, + AccessMode_ReadOnly, pMedium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + else if (cmd == CMD_FLOPPY) + hrc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy, + AccessMode_ReadOnly, pMedium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + Utf8Str strParentUUID(Disk::tr("base")); + ComPtr pParent; + pMedium->COMGETTER(Parent)(pParent.asOutParam()); + if (!pParent.isNull()) + { + Bstr bstrParentUUID; + pParent->COMGETTER(Id)(bstrParentUUID.asOutParam()); + strParentUUID = bstrParentUUID; + } + + hrc = showMediumInfo(a->virtualBox, pMedium, strParentUUID.c_str(), true); + + return SUCCEEDED(hrc) ? 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 hrc = 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(Disk::tr("Only one command can be specified: '%s'"), ValueUnion.psz); + cmd = CMD_DISK; + break; + + case 'D': // DVD + if (cmd != CMD_NONE) + return errorSyntax(Disk::tr("Only one command can be specified: '%s'"), ValueUnion.psz); + cmd = CMD_DVD; + break; + + case 'f': // floppy + if (cmd != CMD_NONE) + return errorSyntax(Disk::tr("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(Disk::tr("Invalid parameter '%s'"), ValueUnion.psz); + break; + + default: + if (c > 0) + { + if (RT_C_IS_PRINT(c)) + return errorSyntax(Disk::tr("Invalid option -%c"), c); + else + return errorSyntax(Disk::tr("Invalid option case %i"), c); + } + else if (c == VERR_GETOPT_UNKNOWN_OPTION) + return errorSyntax(Disk::tr("unknown option: %s\n"), ValueUnion.psz); + else if (ValueUnion.pDef) + return errorSyntax("%s: %Rrs", ValueUnion.pDef->pszLong, c); + else + return errorSyntax(Disk::tr("error: %Rrs"), c); + } + } + + /* check for required options */ + if (cmd == CMD_NONE) + cmd = CMD_DISK; + if (!pszFilenameOrUuid) + return errorSyntax(Disk::tr("Medium name or UUID required")); + + ComPtr pMedium; + if (cmd == CMD_DISK) + hrc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk, + AccessMode_ReadWrite, pMedium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + else if (cmd == CMD_DVD) + hrc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD, + AccessMode_ReadOnly, pMedium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + else if (cmd == CMD_FLOPPY) + hrc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy, + AccessMode_ReadWrite, pMedium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + + if (SUCCEEDED(hrc) && pMedium) + { + if (fDelete) + { + ComPtr pProgress; + CHECK_ERROR(pMedium, DeleteStorage(pProgress.asOutParam())); + if (SUCCEEDED(hrc)) + { + hrc = showProgress(pProgress); + CHECK_PROGRESS_ERROR(pProgress, (Disk::tr("Failed to delete medium"))); + } + else + RTMsgError(Disk::tr("Failed to delete medium. Error code %Rhrc"), hrc); + } + CHECK_ERROR(pMedium, Close()); + } + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +RTEXITCODE handleMediumProperty(HandlerArg *a) +{ + HRESULT hrc = 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 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((Disk::tr("unexpected parameter %s\n"), pszCmd)); + cmd = CMD_DISK; + } + a->argv++; + a->argc--; + } + else + { + pszCmd = NULL; + cmd = CMD_DISK; + } + + if (a->argc == 0) + return errorSyntax(Disk::tr("Missing action")); + + pszAction = a->argv[0]; + if ( RTStrICmp(pszAction, "set") + && RTStrICmp(pszAction, "get") + && RTStrICmp(pszAction, "delete")) + return errorSyntax(Disk::tr("Invalid action given: %s"), pszAction); + + if ( ( !RTStrICmp(pszAction, "set") + && a->argc != 4) + || ( RTStrICmp(pszAction, "set") + && a->argc != 3)) + return errorSyntax(Disk::tr("Invalid number of arguments given for action: %s"), pszAction); + + pszFilenameOrUuid = a->argv[1]; + pszProperty = a->argv[2]; + + if (cmd == CMD_DISK) + hrc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk, + AccessMode_ReadWrite, pMedium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + else if (cmd == CMD_DVD) + hrc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD, + AccessMode_ReadOnly, pMedium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + else if (cmd == CMD_FLOPPY) + hrc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy, + AccessMode_ReadWrite, pMedium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + if (SUCCEEDED(hrc) && !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")) + { + /* + * Trigger a call to Medium::i_queryInfo()->VDOpen()->pfnOpen() to + * open the virtual device and populate its properties for + * Medium::getProperty() to retrieve. + */ + MediumState_T state; + CHECK_ERROR(pMedium, RefreshState(&state)); + + Bstr strVal; + CHECK_ERROR(pMedium, GetProperty(Bstr(pszProperty).raw(), strVal.asOutParam())); + if (SUCCEEDED(hrc)) + RTPrintf("%s=%ls\n", pszProperty, strVal.raw()); + } + else if (!RTStrICmp(pszAction, "delete")) + { + CHECK_ERROR(pMedium, SetProperty(Bstr(pszProperty).raw(), Bstr().raw())); + /** @todo */ + } + } + + return SUCCEEDED(hrc) ? 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 hrc; + ComPtr 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(Disk::tr("Invalid parameter '%s'"), ValueUnion.psz); + break; + + default: + if (c > 0) + { + if (RT_C_IS_PRINT(c)) + return errorSyntax(Disk::tr("Invalid option -%c"), c); + else + return errorSyntax(Disk::tr("Invalid option case %i"), c); + } + else if (c == VERR_GETOPT_UNKNOWN_OPTION) + return errorSyntax(Disk::tr("unknown option: %s\n"), ValueUnion.psz); + else if (ValueUnion.pDef) + return errorSyntax("%s: %Rrs", ValueUnion.pDef->pszLong, c); + else + return errorSyntax(Disk::tr("error: %Rrs"), c); + } + } + + if (!pszFilenameOrUuid) + return errorSyntax(Disk::tr("Disk name or UUID required")); + + if (!pszPasswordNew && !pszPasswordOld) + return errorSyntax(Disk::tr("No password specified")); + + if ( (pszPasswordNew && !pszNewPasswordId) + || (!pszPasswordNew && pszNewPasswordId)) + return errorSyntax(Disk::tr("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, Disk::tr("Enter new password:")); + if (rcExit == RTEXITCODE_FAILURE) + return rcExit; + } + else + { + RTEXITCODE rcExit = readPasswordFile(pszPasswordNew, &strPasswordNew); + if (rcExit == RTEXITCODE_FAILURE) + { + RTMsgError(Disk::tr("Failed to read new password from file")); + return rcExit; + } + } + } + + if (pszPasswordOld) + { + if (!RTStrCmp(pszPasswordOld, "-")) + { + /* Get password from console. */ + RTEXITCODE rcExit = readPasswordFromConsole(&strPasswordOld, Disk::tr("Enter old password:")); + if (rcExit == RTEXITCODE_FAILURE) + return rcExit; + } + else + { + RTEXITCODE rcExit = readPasswordFile(pszPasswordOld, &strPasswordOld); + if (rcExit == RTEXITCODE_FAILURE) + { + RTMsgError(Disk::tr("Failed to read old password from file")); + return rcExit; + } + } + } + + /* Always open the medium if necessary, there is no other way. */ + hrc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk, + AccessMode_ReadWrite, hardDisk, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + if (hardDisk.isNull()) + return RTMsgErrorExit(RTEXITCODE_FAILURE, Disk::tr("Invalid hard disk reference, avoiding crash")); + + ComPtr progress; + CHECK_ERROR(hardDisk, ChangeEncryption(Bstr(strPasswordOld).raw(), Bstr(pszCipher).raw(), + Bstr(strPasswordNew).raw(), Bstr(pszNewPasswordId).raw(), + progress.asOutParam())); + if (SUCCEEDED(hrc)) + hrc = showProgress(progress); + if (FAILED(hrc)) + { + if (hrc == E_NOTIMPL) + RTMsgError(Disk::tr("Encrypt hard disk operation is not implemented!")); + else if (hrc == VBOX_E_NOT_SUPPORTED) + RTMsgError(Disk::tr("Encrypt hard disk operation for this cipher is not implemented yet!")); + else if (!progress.isNull()) + CHECK_PROGRESS_ERROR(progress, (Disk::tr("Failed to encrypt hard disk"))); + else + RTMsgError(Disk::tr("Failed to encrypt hard disk!")); + } + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +RTEXITCODE handleCheckMediumPassword(HandlerArg *a) +{ + HRESULT hrc; + ComPtr hardDisk; + const char *pszFilenameOrUuid = NULL; + Utf8Str strPassword; + + if (a->argc != 2) + return errorSyntax(Disk::tr("Invalid number of arguments: %d"), a->argc); + + pszFilenameOrUuid = a->argv[0]; + + if (!RTStrCmp(a->argv[1], "-")) + { + /* Get password from console. */ + RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, Disk::tr("Enter password:")); + if (rcExit == RTEXITCODE_FAILURE) + return rcExit; + } + else + { + RTEXITCODE rcExit = readPasswordFile(a->argv[1], &strPassword); + if (rcExit == RTEXITCODE_FAILURE) + { + RTMsgError(Disk::tr("Failed to read password from file")); + return rcExit; + } + } + + /* Always open the medium if necessary, there is no other way. */ + hrc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk, + AccessMode_ReadWrite, hardDisk, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + if (hardDisk.isNull()) + return RTMsgErrorExit(RTEXITCODE_FAILURE, Disk::tr("Invalid hard disk reference, avoiding crash")); + + CHECK_ERROR(hardDisk, CheckEncryptionPassword(Bstr(strPassword).raw())); + if (SUCCEEDED(hrc)) + RTPrintf(Disk::tr("The given password is correct\n")); + return SUCCEEDED(hrc) ? 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 &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(Disk::tr("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, Disk::tr("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 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 vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + RTGETOPTUNION ValueUnion; + while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (vrc) + { + MEDIUMIOCOMMONOPT_CASES(pCommonOpts); + + case 'q': + fQuick = true; + break; + + default: + return errorGetOpt(vrc, &ValueUnion); + } + } + + /* + * Open the medium for I/O and format it. + */ + ComPtr 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 vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + RTGETOPTUNION ValueUnion; + while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (vrc) + { + 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(vrc, &ValueUnion); + } + } + + /* + * Open the medium for I/O. + */ + ComPtr 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')) + { + vrc = RTStrmOpen(pszOutput, fHex ? "wt" : "wb", &pOut); + if (RT_FAILURE(vrc)) + rcExit = RTMsgErrorExitFailure(Disk::tr("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(Disk::tr("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 SafeArrayBuf; + HRESULT hrc = ptrMediumIO->Read(off, cbToRead, ComSafeArrayAsOutParam(SafeArrayBuf)); + if (FAILED(hrc)) + { + RTStrPrintf(szLine, sizeof(szLine), Disk::tr("Read(%zu bytes at %#RX64)", "", cbToRead), 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(); + if (!fHex) + vrc = RTStrmWrite(pOut, pbBuf, cbReturned); + else + { + /* hexdump -C */ + vrc = VINF_SUCCESS; + 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, Disk::tr("********** \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(Disk::tr("Error writing to '%s': %Rrc"), pszOutput, vrc); + break; + } + } + + /* Advance. */ + if (cbReturned != cbToRead) + { + rcExit = RTMsgErrorExitFailure(Disk::tr("Expected read() at offset %RU64 (%#RX64) to return %#zx bytes, only got %#zx!\n", + "", cbReturned), + off, off, cbReturned, cbToRead); + break; + } + off += cbReturned; + cb -= cbReturned; + } + + /* + * Close output. + */ + if (pOut != g_pStdOut) + { + vrc = RTStrmClose(pOut); + if (RT_FAILURE(vrc)) + rcExit = RTMsgErrorExitFailure(Disk::tr("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 vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + RTGETOPTUNION ValueUnion; + while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (vrc) + { + MEDIUMIOCOMMONOPT_CASES(pCommonOpts); + + case 'O': + pszOutput = ValueUnion.psz; + break; + case 'F': + strFormat = ValueUnion.psz; + break; + case 'v': // --variant + { + vrc = parseMediumVariant(ValueUnion.psz, &enmMediumVariant); + if (RT_FAILURE(vrc)) + return errorArgument(Disk::tr("Invalid medium variant '%s'"), ValueUnion.psz); + break; + } + + default: + return errorGetOpt(vrc, &ValueUnion); + } + } + + /* + * Open the medium for I/O. + */ + ComPtr 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')) + { + vrc = RTStrmOpen(pszOutput, "wb", &pOut); + if (RT_FAILURE(vrc)) + rcExit = RTMsgErrorExitFailure(Disk::tr("Error opening '%s' for writing: %Rrc"), pszOutput, vrc); + } + else + { + pOut = g_pStdOut; + RTStrmSetMode(pOut, true, -1); + } + + if (rcExit == RTEXITCODE_SUCCESS) + { + ComPtr ptrDataStream; + ComPtr ptrProgress; + + com::SafeArray l_variants(sizeof(MediumVariant_T)*8); + + for (ULONG i = 0; i < l_variants.size(); ++i) + { + ULONG temp = enmMediumVariant; + temp &= 1<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 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(); + vrc = RTStrmWrite(pOut, pbBuf, cbReturned); + if (RT_FAILURE(vrc)) + { + rcExit = RTMsgErrorExitFailure(Disk::tr("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) + { + vrc = RTStrmClose(pOut); + if (RT_FAILURE(vrc)) + rcExit = RTMsgErrorExitFailure(Disk::tr("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 vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + RTGETOPTUNION ValueUnion; + while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (vrc) + { + 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(vrc, &ValueUnion); + } + } + return errorNoSubcommand(); +} diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp new file mode 100644 index 00000000..f5a1e446 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp @@ -0,0 +1,3707 @@ +/* $Id: VBoxManageGuestCtrl.cpp $ */ +/** @file + * VBoxManage - Implementation of guestcontrol command. + */ + +/* + * Copyright (C) 2010-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxManage.h" +#include "VBoxManageGuestCtrl.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include /* For RTProcSelf(). */ +#include +#include +#include + +#include + +#include +#include + +#ifdef USE_XPCOM_QUEUE +# include +# include +#endif + +#include + +#ifdef RT_OS_DARWIN +# include +#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() \ + { "--user", GCTLCMD_COMMON_OPT_USER, RTGETOPT_REQ_STRING }, \ + { "--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; +/** Event semaphore used for wait notifications. + * Also being used for the listener implementations in VBoxManageGuestCtrlListener.cpp. */ + RTSEMEVENT g_SemEventGuestCtrlCanceled = NIL_RTSEMEVENT; + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Listener declarations. + */ +VBOX_LISTENER_DECLARE(GuestFileEventListenerImpl) +VBOX_LISTENER_DECLARE(GuestProcessEventListenerImpl) +VBOX_LISTENER_DECLARE(GuestSessionEventListenerImpl) +VBOX_LISTENER_DECLARE(GuestEventListenerImpl) +VBOX_LISTENER_DECLARE(GuestAdditionsRunlevelListener) + +/** + * 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 sub-command scope flags. */ + uint64_t fSubcommandScope; + /** 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 pGuest; + /** Pointer to the to be used guest session. */ + ComPtr 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 > DESTDIRMAP, *PDESTDIRMAP; +typedef std::map< Utf8Str, std::vector >::iterator DESTDIRMAPITER, *PDESTDIRMAPITER; + + +enum kStreamTransform +{ + kStreamTransform_None = 0, + kStreamTransform_Dos2Unix, + kStreamTransform_Unix2Dos +}; + + +DECLARE_TRANSLATION_CONTEXT(GuestCtrl); + + +#ifdef RT_OS_WINDOWS +static BOOL WINAPI gctlSignalHandler(DWORD dwCtrlType) RT_NOTHROW_DEF +{ + 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); + RTSemEventSignal(g_SemEventGuestCtrlCanceled); + 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) RT_NOTHROW_DEF +{ + RT_NOREF(iSignal); + ASMAtomicWriteBool(&g_fGuestCtrlCanceled, true); + RTSemEventSignal(g_SemEventGuestCtrlCanceled); +} +#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 vrc = VINF_SUCCESS; +#ifdef RT_OS_WINDOWS + if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)gctlSignalHandler, TRUE /* Add handler */)) + { + vrc = RTErrConvertFromWin32(GetLastError()); + RTMsgError(GuestCtrl::tr("Unable to install console control handler, vrc=%Rrc\n"), vrc); + } +#else + signal(SIGINT, gctlSignalHandler); + signal(SIGTERM, gctlSignalHandler); +# ifdef SIGBREAK + signal(SIGBREAK, gctlSignalHandler); +# endif +#endif + + if (RT_SUCCESS(vrc)) + vrc = RTSemEventCreate(&g_SemEventGuestCtrlCanceled); + + return vrc; +} + + +/** + * Uninstalls a previously installed signal handler. + */ +static int gctlSignalHandlerUninstall(void) +{ + int vrc = VINF_SUCCESS; +#ifdef RT_OS_WINDOWS + if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)NULL, FALSE /* Remove handler */)) + { + vrc = RTErrConvertFromWin32(GetLastError()); + RTMsgError(GuestCtrl::tr("Unable to uninstall console control handler, vrc=%Rrc\n"), vrc); + } +#else + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); +# ifdef SIGBREAK + signal(SIGBREAK, SIG_DFL); +# endif +#endif + + if (g_SemEventGuestCtrlCanceled != NIL_RTSEMEVENT) + { + RTSemEventDestroy(g_SemEventGuestCtrlCanceled); + g_SemEventGuestCtrlCanceled = NIL_RTSEMEVENT; + } + return vrc; +} + + +/** + * Translates a process status to a human readable string. + * + * @sa GuestProcess::i_statusToString() + */ +const char *gctlProcessStatusToText(ProcessStatus_T enmStatus) +{ + switch (enmStatus) + { + case ProcessStatus_Starting: + return GuestCtrl::tr("starting"); + case ProcessStatus_Started: + return GuestCtrl::tr("started"); + case ProcessStatus_Paused: + return GuestCtrl::tr("paused"); + case ProcessStatus_Terminating: + return GuestCtrl::tr("terminating"); + case ProcessStatus_TerminatedNormally: + return GuestCtrl::tr("successfully terminated"); + case ProcessStatus_TerminatedSignal: + return GuestCtrl::tr("terminated by signal"); + case ProcessStatus_TerminatedAbnormally: + return GuestCtrl::tr("abnormally aborted"); + case ProcessStatus_TimedOutKilled: + return GuestCtrl::tr("timed out"); + case ProcessStatus_TimedOutAbnormally: + return GuestCtrl::tr("timed out, hanging"); + case ProcessStatus_Down: + return GuestCtrl::tr("killed"); + case ProcessStatus_Error: + return GuestCtrl::tr("error"); + default: + break; + } + return GuestCtrl::tr("unknown"); +} + +/** + * Translates a guest process wait result to a human readable string. + */ +static const char *gctlProcessWaitResultToText(ProcessWaitResult_T enmWaitResult) +{ + switch (enmWaitResult) + { + case ProcessWaitResult_Start: + return GuestCtrl::tr("started"); + case ProcessWaitResult_Terminate: + return GuestCtrl::tr("terminated"); + case ProcessWaitResult_Status: + return GuestCtrl::tr("status changed"); + case ProcessWaitResult_Error: + return GuestCtrl::tr("error"); + case ProcessWaitResult_Timeout: + return GuestCtrl::tr("timed out"); + case ProcessWaitResult_StdIn: + return GuestCtrl::tr("stdin ready"); + case ProcessWaitResult_StdOut: + return GuestCtrl::tr("data on stdout"); + case ProcessWaitResult_StdErr: + return GuestCtrl::tr("data on stderr"); + case ProcessWaitResult_WaitFlagNotSupported: + return GuestCtrl::tr("waiting flag not supported"); + default: + break; + } + return GuestCtrl::tr("unknown"); +} + +/** + * Translates a guest session status to a human readable string. + */ +const char *gctlGuestSessionStatusToText(GuestSessionStatus_T enmStatus) +{ + switch (enmStatus) + { + case GuestSessionStatus_Starting: + return GuestCtrl::tr("starting"); + case GuestSessionStatus_Started: + return GuestCtrl::tr("started"); + case GuestSessionStatus_Terminating: + return GuestCtrl::tr("terminating"); + case GuestSessionStatus_Terminated: + return GuestCtrl::tr("terminated"); + case GuestSessionStatus_TimedOutKilled: + return GuestCtrl::tr("timed out"); + case GuestSessionStatus_TimedOutAbnormally: + return GuestCtrl::tr("timed out, hanging"); + case GuestSessionStatus_Down: + return GuestCtrl::tr("killed"); + case GuestSessionStatus_Error: + return GuestCtrl::tr("error"); + default: + break; + } + return GuestCtrl::tr("unknown"); +} + +/** + * Translates a guest file status to a human readable string. + */ +const char *gctlFileStatusToText(FileStatus_T enmStatus) +{ + switch (enmStatus) + { + case FileStatus_Opening: + return GuestCtrl::tr("opening"); + case FileStatus_Open: + return GuestCtrl::tr("open"); + case FileStatus_Closing: + return GuestCtrl::tr("closing"); + case FileStatus_Closed: + return GuestCtrl::tr("closed"); + case FileStatus_Down: + return GuestCtrl::tr("killed"); + case FileStatus_Error: + return GuestCtrl::tr("error"); + default: + break; + } + return GuestCtrl::tr("unknown"); +} + +/** + * Translates a file system objec type to a string. + */ +const char *gctlFsObjTypeToName(FsObjType_T enmType) +{ + switch (enmType) + { + case FsObjType_Unknown: return GuestCtrl::tr("unknown"); + case FsObjType_Fifo: return GuestCtrl::tr("fifo"); + case FsObjType_DevChar: return GuestCtrl::tr("char-device"); + case FsObjType_Directory: return GuestCtrl::tr("directory"); + case FsObjType_DevBlock: return GuestCtrl::tr("block-device"); + case FsObjType_File: return GuestCtrl::tr("file"); + case FsObjType_Symlink: return GuestCtrl::tr("symlink"); + case FsObjType_Socket: return GuestCtrl::tr("socket"); + case FsObjType_WhiteOut: return GuestCtrl::tr("white-out"); +#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK + case FsObjType_32BitHack: break; +#endif + } + return GuestCtrl::tr("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(GuestCtrl::tr("Error details:")); + GluePrintErrorInfo(errorInfo); + } + return VERR_GENERAL_FAILURE; /** @todo */ + } + AssertMsgFailedReturn((GuestCtrl::tr("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 pProgress) +{ + int vrc = VINF_SUCCESS; + HRESULT hrc; + + 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(hrc), (GuestCtrl::tr("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) +{ + pCtx->pArg = pArg; + pCtx->pCmdDef = NULL; + pCtx->pszVmNameOrUuid = NULL; + pCtx->fPostOptionParsingInited = false; + pCtx->fLockedVmSession = false; + pCtx->fDetachGuestSession = false; + pCtx->fInstalledSignalHandler = false; + pCtx->cVerbose = 0; + pCtx->strUsername.setNull(); + pCtx->strPassword.setNull(); + pCtx->strDomain.setNull(); + pCtx->pGuest.setNull(); + pCtx->pGuestSession.setNull(); + pCtx->uSessionID = 0; + + /* + * The user name defaults to the host one, if we can get at it. + */ + char szUser[1024]; + int vrc = RTProcQueryUsername(RTProcSelf(), szUser, sizeof(szUser), NULL); + if ( RT_SUCCESS(vrc) + && RTStrIsValidEncoding(szUser)) /* paranoia was required on posix at some point, not needed any more! */ + { + try + { + pCtx->strUsername = szUser; + } + catch (std::bad_alloc &) + { + return RTMsgErrorExit(RTEXITCODE_FAILURE, GuestCtrl::tr("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(GuestCtrl::tr("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(GuestCtrl::tr("Password is given more than once.")); + pCtx->strPassword = pValueUnion->psz; + } + else + RTMsgWarning(GuestCtrl::tr("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(GuestCtrl::tr("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(GuestCtrl::tr("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 hrc; + AssertPtr(pCtx); + AssertPtr(pCtx->pArg); + + /* + * Find the VM and check if it's running. + */ + ComPtr machine; + CHECK_ERROR(pCtx->pArg->virtualBox, FindMachine(Bstr(pCtx->pszVmNameOrUuid).raw(), machine.asOutParam())); + if (SUCCEEDED(hrc)) + { + MachineState_T enmMachineState; + CHECK_ERROR(machine, COMGETTER(State)(&enmMachineState)); + if ( SUCCEEDED(hrc) + && 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(hrc)) + { + pCtx->fLockedVmSession = true; + ComPtr ptrConsole; + CHECK_ERROR(pCtx->pArg->session, COMGETTER(Console)(ptrConsole.asOutParam())); + if (SUCCEEDED(hrc)) + { + if (ptrConsole.isNotNull()) + { + CHECK_ERROR(ptrConsole, COMGETTER(Guest)(pCtx->pGuest.asOutParam())); + if (SUCCEEDED(hrc)) + return RTEXITCODE_SUCCESS; + } + else + RTMsgError(GuestCtrl::tr("Failed to get a IConsole pointer for the machine. Is it still running?\n")); + } + } + } + else if (SUCCEEDED(hrc)) + RTMsgError(GuestCtrl::tr("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 hrc; + 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, + GuestCtrl::tr("[%RU32] VBoxManage Guest Control [%s] - %s"), + RTProcSelf(), pCtx->pszVmNameOrUuid, pCtx->pCmdDef->pszName) < 0) + return RTMsgErrorExit(RTEXITCODE_FAILURE, GuestCtrl::tr("No enough memory for session name")); + + /* + * Create a guest session. + */ + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("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(GuestCtrl::tr("Out of memory setting up IGuest::CreateSession call")); + hrc = E_OUTOFMEMORY; + } + if (SUCCEEDED(hrc)) + { + /* + * Wait for guest session to start. + */ + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Waiting for guest session to start...\n")); + GuestSessionWaitResult_T enmWaitResult = GuestSessionWaitResult_None; /* Shut up MSC */ + try + { + com::SafeArray 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(GuestCtrl::tr("Out of memory setting up IGuestSession::WaitForArray call")); + hrc = E_OUTOFMEMORY; + } + if (SUCCEEDED(hrc)) + { + /* 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(hrc)) + { + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("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(GuestCtrl::tr("Error starting guest session (current status is: %s)\n"), + SUCCEEDED(hrc) ? gctlGuestSessionStatusToText(enmSessionStatus) : GuestCtrl::tr("")); + } + } + } + + 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 vrc = gctlSignalHandlerInstall(); + pCtx->fInstalledSignalHandler = RT_SUCCESS(vrc); + } + } + } + else + rcExit = errorSyntax(GuestCtrl::tr("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 hrc; + 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(GuestCtrl::tr("Closing guest session ...\n")); + + CHECK_ERROR(pCtx->pGuestSession, Close()); + } + else if ( pCtx->fDetachGuestSession + && pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("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. Can be the bit bucket or a (valid [std]) handle. + * @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 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(GuestCtrl::tr("Unable to write output, vrc=%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 to std handles, + * or going to the bit bucket instead. + * @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(GuestCtrl::tr("Unsupported %s line ending conversion"), pszName); + /** @todo Implement dos2unix and unix2dos stream filters. */ + } + return true; + } + RTMsgWarning(GuestCtrl::tr("Error getting %s handle: %Rrc"), pszName, vrc); + } + else /* If disabled, all goes to / gets fed to/from the bit bucket. */ + { + RTFILE hFile; + int vrc = RTFileOpenBitBucket(&hFile, enmHandle == RTHANDLESTD_INPUT ? RTFILE_O_READ : RTFILE_O_WRITE); + if (RT_SUCCESS(vrc)) + { + vrc = RTVfsIoStrmFromRTFile(hFile, 0 /* fOpen */, false /* fLeaveOpen */, phVfsIos); + if (RT_SUCCESS(vrc)) + return true; + } + } + + 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'. + */ +static RTEXITCODE gctlHandleRunCommon(PGCTLCMDCTX pCtx, int argc, char **argv, bool fRunCmd) +{ + 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() + { "--arg0", '0', RTGETOPT_REQ_STRING }, + { "--putenv", 'E', RTGETOPT_REQ_STRING }, + { "--exe", 'e', RTGETOPT_REQ_STRING }, + { "--timeout", 't', RTGETOPT_REQ_UINT32 }, + { "--unquoted-args", 'u', RTGETOPT_REQ_NOTHING }, + { "--ignore-orphaned-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 aCreateFlags; + com::SafeArray aWaitFlags; + com::SafeArray aArgs; + com::SafeArray aEnv; + const char * pszImage = NULL; + const char * pszArg0 = NULL; /* Argument 0 to use. pszImage will be used if not specified. */ + 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 errorSyntax(GuestCtrl::tr("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(GuestCtrl::tr("Warning: Deprecated option \"--no-profile\" specified\n")); + break; + + case kGstCtrlRunOpt_Profile: + aCreateFlags.push_back(ProcessCreateFlag_Profile); + break; + + case '0': + pszArg0 = ValueUnion.psz; + 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: + /* VINF_GETOPT_NOT_OPTION comes after all options have been specified; + * so if pszImage still is zero at this stage, we use the first non-option found + * as the image being executed. */ + if (!pszImage) + pszImage = ValueUnion.psz; + else /* Add anything else to the arguments vector. */ + aArgs.push_back(Bstr(ValueUnion.psz).raw()); + break; + + default: + return errorGetOpt(ch, &ValueUnion); + + } /* switch */ + } /* while RTGetOpt */ + + /* Must have something to execute. */ + if (!pszImage || !*pszImage) + return errorSyntax(GuestCtrl::tr("No executable specified!")); + + /* Set the arg0 argument (descending precedence): + * - If an argument 0 is explicitly specified (via "--arg0"), use this as argument 0. + * - When an image is specified explicitly (via "--exe "), use as argument 0. + * Note: This is (and ever was) the default behavior users expect, so don't change this! */ + if (pszArg0) + aArgs.push_front(Bstr(pszArg0).raw()); + else + aArgs.push_front(Bstr(pszImage).raw()); + + if (pCtx->cVerbose) /* Print the final execution parameters in verbose mode. */ + { + RTPrintf(GuestCtrl::tr("Executing:\n Image : %s\n"), pszImage); + for (size_t i = 0; i < aArgs.size(); i++) + RTPrintf(GuestCtrl::tr(" arg[%d]: %ls\n"), i, aArgs[i]); + } + /* No altering of aArgs and/or pszImage after this point! */ + + /* + * 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 hrc; + + 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(GuestCtrl::tr("Starting guest process ...\n")); + else + RTPrintf(GuestCtrl::tr("Starting guest process (within %ums)\n"), cMsTimeout); + } + ComPtr 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 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(GuestCtrl::tr("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(GuestCtrl::tr("[%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)); + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Wait result is '%s' (%d)\n"), gctlProcessWaitResultToText(waitResult), waitResult); + switch (waitResult) + { + case ProcessWaitResult_Start: /** @todo you always wait for 'start', */ + fCompletedStartCmd = fCompleted = !fRunCmd; /* Only wait for startup if the 'start' command. */ + if (!fCompleted && aWaitFlags[0] == ProcessWaitForFlag_Start) + aWaitFlags[0] = ProcessWaitForFlag_Terminate; + break; + case ProcessWaitResult_StdOut: + fReadStdOut = true; + break; + case ProcessWaitResult_StdErr: + fReadStdErr = true; + break; + case ProcessWaitResult_Terminate: + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("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; + /* Note: In case the user specified explicitly not wanting to wait for stdout / stderr, + * the configured VFS handle goes to / will be fed from the bit bucket. */ + 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(GuestCtrl::tr("Process execution aborted!\n")); + rcExit = EXITCODEEXEC_CANCELED; + } + else if (fCompletedStartCmd) + { + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("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(GuestCtrl::tr("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(GuestCtrl::tr("Process timed out (guest side) and %s\n"), + procStatus == ProcessStatus_TimedOutAbnormally + ? GuestCtrl::tr("failed to terminate so far") : GuestCtrl::tr("was terminated")); + rcExit = EXITCODEEXEC_TIMEOUT; + } + else + { + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Process now is in status [%s] (unexpected)\n"), + gctlProcessStatusToText(procStatus)); + rcExit = RTEXITCODE_FAILURE; + } + } + else if (RT_FAILURE_NP(vrc)) + { + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Process monitor loop quit with vrc=%Rrc\n"), vrc); + rcExit = RTEXITCODE_FAILURE; + } + else + { + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Process monitor loop timed out\n")); + rcExit = EXITCODEEXEC_TIMEOUT; + } + + } while (0); + } + catch (std::bad_alloc &) + { + hrc = 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(hrc) && !g_fGuestCtrlCanceled) + pCtx->fDetachGuestSession = true; + + /* Make sure we return failure on failure. */ + if (FAILED(hrc) && 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*/); +} + + +static DECLCALLBACK(RTEXITCODE) gctlHandleStart(PGCTLCMDCTX pCtx, int argc, char **argv) +{ + return gctlHandleRunCommon(pCtx, argc, argv, false /*fRunCmd*/); +} + + +static RTEXITCODE gctlHandleCopy(PGCTLCMDCTX pCtx, int argc, char **argv, bool fHostToGuest) +{ + AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); + + /* + * 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. + */ + static const RTGETOPTDEF s_aOptions[] = + { + GCTLCMD_COMMON_OPTION_DEFS() + { "--follow", 'L', RTGETOPT_REQ_NOTHING }, /* Kept for backwards-compatibility (VBox < 7.0). */ + { "--dereference", 'L', RTGETOPT_REQ_NOTHING }, + { "--no-replace", 'n', RTGETOPT_REQ_NOTHING }, /* like "-n" via cp. */ + { "--recursive", 'R', RTGETOPT_REQ_NOTHING }, + { "--target-directory", 't', RTGETOPT_REQ_STRING }, + { "--update", 'u', RTGETOPT_REQ_NOTHING } /* like "-u" via cp. */ + }; + + 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; + bool fUpdate = false; /* Whether to copy the file only if it's newer than the target. */ + bool fNoReplace = false; /* Only copy the file if it does not exist yet. */ + + 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 'L': + if (!RTStrICmp(ValueUnion.pDef->pszLong, "--follow")) + RTMsgWarning("--follow is deprecated; use --dereference instead."); + fFollow = true; + break; + + case 'n': + fNoReplace = true; + break; + + case 'R': + fRecursive = true; + break; + + case 't': + pszDst = ValueUnion.psz; + fDstMustBeDir = true; + break; + + case 'u': + fUpdate = true; + break; + + default: + return errorGetOpt(ch, &ValueUnion); + } + } + + char **papszSources = RTGetOptNonOptionArrayPtr(&GetState); + size_t cSources = &argv[argc] - papszSources; + + if (!cSources) + return errorSyntax(GuestCtrl::tr("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 errorSyntax(GuestCtrl::tr("No destination specified!")); + + char szAbsDst[RTPATH_MAX]; + if (!fHostToGuest) + { + vrc = RTPathAbs(pszDst, szAbsDst, sizeof(szAbsDst)); + if (RT_SUCCESS(vrc)) + pszDst = szAbsDst; + else + return RTMsgErrorExitFailure(GuestCtrl::tr("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(GuestCtrl::tr("Copying from host to guest ...\n")); + else + RTPrintf(GuestCtrl::tr("Copying from guest to host ...\n")); + } + + HRESULT hrc = S_OK; + + com::SafeArray aSources; + com::SafeArray aFilters; /** @todo Populate those? For now we use caller-based globbing. */ + com::SafeArray aCopyFlags; + + size_t iSrc = 0; + for (; iSrc < cSources; iSrc++) + { + aSources.push_back(Bstr(papszSources[iSrc]).raw()); + aFilters.push_back(Bstr("").raw()); /* Empty for now. See @todo above. */ + + /* Compile the comma-separated list of flags. + * Certain flags are only available for specific file system objects, e.g. directories. */ + bool fIsDir = false; + if (fHostToGuest) + { + RTFSOBJINFO ObjInfo; + vrc = RTPathQueryInfo(papszSources[iSrc], &ObjInfo, RTFSOBJATTRADD_NOTHING); + if (RT_SUCCESS(vrc)) + fIsDir = RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode); + + if (RT_FAILURE(vrc)) + break; + } + else /* Guest to host. */ + { + ComPtr pFsObjInfo; + hrc = pCtx->pGuestSession->FsObjQueryInfo(Bstr(papszSources[iSrc]).raw(), RT_BOOL(fFollow) /* fFollowSymlinks */, + pFsObjInfo.asOutParam()); + if (SUCCEEDED(hrc)) + { + FsObjType_T enmObjType; + CHECK_ERROR(pFsObjInfo,COMGETTER(Type)(&enmObjType)); + if (SUCCEEDED(hrc)) + { + /* Take action according to source file. */ + fIsDir = enmObjType == FsObjType_Directory; + } + } + + if (FAILED(hrc)) + { + vrc = gctlPrintError(pCtx->pGuestSession, COM_IIDOF(IGuestSession)); + break; + } + } + + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Source '%s' is a %s\n"), papszSources[iSrc], fIsDir ? "directory" : "file"); + + Utf8Str strCopyFlags; + if (fRecursive && fIsDir) /* Only available for directories. Just ignore otherwise. */ + strCopyFlags += "Recursive,"; + if (fFollow) + strCopyFlags += "FollowLinks,"; + if (fUpdate) /* Only copy source files which are newer than the destination file. */ + strCopyFlags += "Update,"; + if (fNoReplace) /* Do not overwrite files. */ + strCopyFlags += "NoReplace,"; + else if (!fNoReplace && fIsDir) + strCopyFlags += "CopyIntoExisting,"; /* Only copy into existing directories if "--no-replace" isn't specified. */ + aCopyFlags.push_back(Bstr(strCopyFlags).raw()); + } + + if (RT_FAILURE(vrc)) + return RTMsgErrorExitFailure(GuestCtrl::tr("Error looking file system information for source '%s', vrc=%Rrc"), + papszSources[iSrc], vrc); + + ComPtr pProgress; + if (fHostToGuest) + { + hrc = pCtx->pGuestSession->CopyToGuest(ComSafeArrayAsInParam(aSources), + ComSafeArrayAsInParam(aFilters), ComSafeArrayAsInParam(aCopyFlags), + Bstr(pszDst).raw(), pProgress.asOutParam()); + } + else /* Guest to host. */ + { + hrc = pCtx->pGuestSession->CopyFromGuest(ComSafeArrayAsInParam(aSources), + ComSafeArrayAsInParam(aFilters), ComSafeArrayAsInParam(aCopyFlags), + Bstr(pszDst).raw(), pProgress.asOutParam()); + } + + if (FAILED(hrc)) + { + vrc = gctlPrintError(pCtx->pGuestSession, COM_IIDOF(IGuestSession)); + } + else if (pProgress.isNotNull()) + { + if (pCtx->cVerbose) + hrc = showProgress(pProgress); + else + hrc = pProgress->WaitForCompletion(-1 /* No timeout */); + if (SUCCEEDED(hrc)) + CHECK_PROGRESS_ERROR(pProgress, (GuestCtrl::tr("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 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(GuestCtrl::tr("Creating %RU32 directories...\n", "", argc - GetState.iNext + 1), + argc - GetState.iNext + 1); + } + if (g_fGuestCtrlCanceled) + return RTMsgErrorExit(RTEXITCODE_FAILURE, GuestCtrl::tr("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(GuestCtrl::tr("Creating directory \"%s\" ...\n"), ValueUnion.psz); + try + { + HRESULT hrc; + CHECK_ERROR(pCtx->pGuestSession, DirectoryCreate(Bstr(ValueUnion.psz).raw(), + fDirMode, ComSafeArrayAsInParam(aDirCreateFlags))); + if (FAILED(hrc)) + rcExit = RTEXITCODE_FAILURE; + } + catch (std::bad_alloc &) + { + return RTMsgErrorExit(RTEXITCODE_FAILURE, GuestCtrl::tr("Out of memory\n")); + } + break; + + default: + return errorGetOpt(ch, &ValueUnion); + } + } + + if (!cDirsCreated) + return errorSyntax(GuestCtrl::tr("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) + { + if (fRecursive) + RTPrintf(GuestCtrl::tr("Removing %RU32 directory tree(s)...\n", "", argc - GetState.iNext + 1), + argc - GetState.iNext + 1); + else + RTPrintf(GuestCtrl::tr("Removing %RU32 directorie(s)...\n", "", argc - GetState.iNext + 1), + argc - GetState.iNext + 1); + } + } + if (g_fGuestCtrlCanceled) + return RTMsgErrorExit(RTEXITCODE_FAILURE, GuestCtrl::tr("rmdir was interrupted by Ctrl-C (%u left)\n"), + argc - GetState.iNext + 1); + + cDirRemoved++; + HRESULT hrc; + if (!fRecursive) + { + /* + * Remove exactly one directory. + */ + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Removing directory \"%s\" ...\n"), ValueUnion.psz); + try + { + CHECK_ERROR(pCtx->pGuestSession, DirectoryRemove(Bstr(ValueUnion.psz).raw())); + } + catch (std::bad_alloc &) + { + return RTMsgErrorExit(RTEXITCODE_FAILURE, GuestCtrl::tr("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(GuestCtrl::tr("Recursively removing directory \"%s\" ...\n"), ValueUnion.psz); + try + { + /** @todo Make flags configurable. */ + com::SafeArray aRemRecFlags; + aRemRecFlags.push_back(DirectoryRemoveRecFlag_ContentAndDir); + + ComPtr ptrProgress; + CHECK_ERROR(pCtx->pGuestSession, DirectoryRemoveRecursive(Bstr(ValueUnion.psz).raw(), + ComSafeArrayAsInParam(aRemRecFlags), + ptrProgress.asOutParam())); + if (SUCCEEDED(hrc)) + { + if (pCtx->cVerbose) + hrc = showProgress(ptrProgress); + else + hrc = ptrProgress->WaitForCompletion(-1 /* indefinitely */); + if (SUCCEEDED(hrc)) + CHECK_PROGRESS_ERROR(ptrProgress, (GuestCtrl::tr("Directory deletion failed"))); + ptrProgress.setNull(); + } + } + catch (std::bad_alloc &) + { + return RTMsgErrorExit(RTEXITCODE_FAILURE, GuestCtrl::tr("Out of memory during recursive rmdir\n")); + } + } + + /* + * This command returns immediately on failure since it's destructive in nature. + */ + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + break; + } + + default: + return errorGetOpt(ch, &ValueUnion); + } + } + + if (!cDirRemoved) + return errorSyntax(GuestCtrl::tr("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(GuestCtrl::tr("Removing %RU32 file(s)...\n", "", argc - GetState.iNext + 1), + argc - GetState.iNext + 1); + } + if (g_fGuestCtrlCanceled) + return RTMsgErrorExit(RTEXITCODE_FAILURE, GuestCtrl::tr("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(GuestCtrl::tr("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 hrc; + CHECK_ERROR(pCtx->pGuestSession, FsObjRemove(Bstr(ValueUnion.psz).raw())); + if (FAILED(hrc) && !fForce) + return RTEXITCODE_FAILURE; + } + catch (std::bad_alloc &) + { + return RTMsgErrorExit(RTEXITCODE_FAILURE, GuestCtrl::tr("Out of memory\n")); + } + break; + + default: + return errorGetOpt(ch, &ValueUnion); + } + } + + if (!cFilesDeleted && !fForce) + return errorSyntax(GuestCtrl::tr("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 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 errorGetOpt(ch, &ValueUnion); + } + } + } + catch (std::bad_alloc &) + { + vrc = VERR_NO_MEMORY; + } + + if (RT_FAILURE(vrc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, GuestCtrl::tr("Failed to initialize, vrc=%Rrc\n"), vrc); + + size_t cSources = vecSources.size(); + if (!cSources) + return errorSyntax(GuestCtrl::tr("No source(s) to move specified!")); + if (cSources < 2) + return errorSyntax(GuestCtrl::tr("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 hrc = S_OK; + + /* Destination must be a directory when specifying multiple sources. */ + if (cSources > 1) + { + ComPtr pFsObjInfo; + hrc = pCtx->pGuestSession->FsObjQueryInfo(Bstr(pszDst).raw(), FALSE /*followSymlinks*/, pFsObjInfo.asOutParam()); + if (FAILED(hrc)) + { + return RTMsgErrorExit(RTEXITCODE_FAILURE, GuestCtrl::tr("Destination does not exist\n")); + } + else + { + FsObjType_T enmObjType = FsObjType_Unknown; /* Shut up MSC */ + hrc = pFsObjInfo->COMGETTER(Type)(&enmObjType); + if (SUCCEEDED(hrc)) + { + if (enmObjType != FsObjType_Directory) + return RTMsgErrorExit(RTEXITCODE_FAILURE, + GuestCtrl::tr("Destination must be a directory when specifying multiple sources\n")); + } + else + return RTMsgErrorExit(RTEXITCODE_FAILURE, + GuestCtrl::tr("Unable to determine destination type: %Rhrc\n"), + hrc); + } + } + + /* + * Rename (move) the entries. + */ + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Renaming %RU32 %s ...\n"), cSources, + cSources > 1 ? GuestCtrl::tr("sources", "", cSources) : GuestCtrl::tr("source")); + + std::vector< Utf8Str >::iterator it = vecSources.begin(); + while ( it != vecSources.end() + && !g_fGuestCtrlCanceled) + { + Utf8Str strSrcCur = (*it); + + ComPtr pFsObjInfo; + FsObjType_T enmObjType = FsObjType_Unknown; /* Shut up MSC */ + hrc = pCtx->pGuestSession->FsObjQueryInfo(Bstr(strSrcCur).raw(), FALSE /*followSymlinks*/, pFsObjInfo.asOutParam()); + if (SUCCEEDED(hrc)) + hrc = pFsObjInfo->COMGETTER(Type)(&enmObjType); + if (FAILED(hrc)) + { + RTPrintf(GuestCtrl::tr("Cannot stat \"%s\": No such file or directory\n"), strSrcCur.c_str()); + ++it; + continue; /* Skip. */ + } + + char *pszDstCur = NULL; + + if (cSources > 1) + { + pszDstCur = RTPathJoinA(pszDst, RTPathFilename(strSrcCur.c_str())); + } + else + pszDstCur = RTStrDup(pszDst); + + AssertPtrBreakStmt(pszDstCur, VERR_NO_MEMORY); + + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Renaming %s \"%s\" to \"%s\" ...\n"), + enmObjType == FsObjType_Directory ? GuestCtrl::tr("directory", "object") : GuestCtrl::tr("file","object"), + strSrcCur.c_str(), pszDstCur); + + if (!fDryrun) + { + CHECK_ERROR(pCtx->pGuestSession, FsObjRename(Bstr(strSrcCur).raw(), + Bstr(pszDstCur).raw(), + ComSafeArrayAsInParam(aRenameFlags))); + /* Keep going with next item in case of errors. */ + } + + RTStrFree(pszDstCur); + + ++it; + } + + if ( (it != vecSources.end()) + && pCtx->cVerbose) + { + RTPrintf(GuestCtrl::tr("Warning: Not all sources were renamed\n")); + } + + return FAILED(hrc) ? 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 errorSyntax(GuestCtrl::tr("More than one template specified!\n")); + break; + + default: + return errorGetOpt(ch, &ValueUnion); + } + } + + if (strTemplate.isEmpty()) + return errorSyntax(GuestCtrl::tr("No template specified!")); + + if (!fDirectory) + return errorSyntax(GuestCtrl::tr("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(GuestCtrl::tr("Creating temporary directory from template '%s' in directory '%s' ...\n"), + strTemplate.c_str(), strTempDir.c_str()); + else if (fDirectory) + RTPrintf(GuestCtrl::tr("Creating temporary directory from template '%s' in default temporary directory ...\n"), + strTemplate.c_str()); + else if (!fDirectory && !strTempDir.isEmpty()) + RTPrintf(GuestCtrl::tr("Creating temporary file from template '%s' in directory '%s' ...\n"), + strTemplate.c_str(), strTempDir.c_str()); + else if (!fDirectory) + RTPrintf(GuestCtrl::tr("Creating temporary file from template '%s' in default temporary directory ...\n"), + strTemplate.c_str()); + } + + HRESULT hrc = S_OK; + if (fDirectory) + { + Bstr bstrDirectory; + CHECK_ERROR(pCtx->pGuestSession, DirectoryCreateTemp(Bstr(strTemplate).raw(), + fMode, Bstr(strTempDir).raw(), + fSecure, + bstrDirectory.asOutParam())); + if (SUCCEEDED(hrc)) + RTPrintf(GuestCtrl::tr("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). */ + hrc = E_FAIL; + } + + return FAILED(hrc) ? 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 errorSyntax(GuestCtrl::tr("Command \"%s\" not implemented yet!"), ValueUnion.psz); + + default: + return errorGetOpt(ch, &ValueUnion); + } + } + + if (ch != VINF_GETOPT_NOT_OPTION) + return errorSyntax(GuestCtrl::tr("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(GuestCtrl::tr("Checking for element \"%s\" ...\n"), ValueUnion.psz); + + ComPtr 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(GuestCtrl::tr("Failed to stat '%s': No such file\n"), ValueUnion.psz); + rcExit = RTEXITCODE_FAILURE; + } + else + { + RTPrintf(GuestCtrl::tr(" 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(GuestCtrl::tr(" Size: %-17RU64 Alloc: %-19RU64 Type: %s\n"), + cbObject, cbAllocated, gctlFsObjTypeToName(enmType)); + RTPrintf(GuestCtrl::tr("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(GuestCtrl::tr(" Mode: %-16s Attrib: %-17s Dev ID: %#RX32\n"), pszMode, pszAttribs, uDeviceNo); + else + RTPrintf(GuestCtrl::tr(" Mode: %-16s Attrib: %s\n"), pszMode, pszAttribs); + + RTPrintf(GuestCtrl::tr(" Owner: %4d/%-12ls Group: %4d/%ls\n"), uid, bstrUsername.raw(), gid, bstrGroupName.raw()); + + RTTIMESPEC TimeSpec; + char szTmp[RTTIME_STR_LEN]; + RTPrintf(GuestCtrl::tr(" Birth: %s\n"), RTTimeSpecToString(RTTimeSpecSetNano(&TimeSpec, nsBirthTime), + szTmp, sizeof(szTmp))); + RTPrintf(GuestCtrl::tr("Change: %s\n"), RTTimeSpecToString(RTTimeSpecSetNano(&TimeSpec, nsChangeTime), + szTmp, sizeof(szTmp))); + RTPrintf(GuestCtrl::tr("Modify: %s\n"), RTTimeSpecToString(RTTimeSpecSetNano(&TimeSpec, nsModificationTime), + szTmp, sizeof(szTmp))); + RTPrintf(GuestCtrl::tr("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; +} + +/** + * Waits for a Guest Additions run level being reached. + * + * @returns VBox status code. + * Returns VERR_CANCELLED if waiting for cancelled due to signal handling, e.g. when CTRL+C or some sort was pressed. + * @param pCtx The guest control command context. + * @param enmRunLevel Run level to wait for. + * @param cMsTimeout Timeout (in ms) for waiting. + */ +static int gctlWaitForRunLevel(PGCTLCMDCTX pCtx, AdditionsRunLevelType_T enmRunLevel, RTMSINTERVAL cMsTimeout) +{ + int vrc = VINF_SUCCESS; /* Shut up MSVC. */ + + try + { + HRESULT hrc = S_OK; + /** Whether we need to actually wait for the run level or if we already reached it. */ + bool fWait = false; + + /* Install an event handler first to catch any runlevel changes. */ + ComObjPtr pGuestListener; + do + { + /* Listener creation. */ + pGuestListener.createObject(); + pGuestListener->init(new GuestAdditionsRunlevelListener(enmRunLevel)); + + /* Register for IGuest events. */ + ComPtr es; + CHECK_ERROR_BREAK(pCtx->pGuest, COMGETTER(EventSource)(es.asOutParam())); + com::SafeArray eventTypes; + eventTypes.push_back(VBoxEventType_OnGuestAdditionsStatusChanged); + CHECK_ERROR_BREAK(es, RegisterListener(pGuestListener, ComSafeArrayAsInParam(eventTypes), + true /* Active listener */)); + + AdditionsRunLevelType_T enmRunLevelCur = AdditionsRunLevelType_None; + CHECK_ERROR_BREAK(pCtx->pGuest, COMGETTER(AdditionsRunLevel)(&enmRunLevelCur)); + fWait = enmRunLevelCur != enmRunLevel; + + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Current run level is %RU32\n"), enmRunLevelCur); + + } while (0); + + if (fWait) + { + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Waiting for run level %RU32 ...\n"), enmRunLevel); + + RTMSINTERVAL tsStart = RTTimeMilliTS(); + while (RTTimeMilliTS() - tsStart < cMsTimeout) + { + /* Wait for the global signal semaphore getting signalled. */ + vrc = RTSemEventWait(g_SemEventGuestCtrlCanceled, 100 /* ms */); + if (RT_FAILURE(vrc)) + { + if (vrc == VERR_TIMEOUT) + continue; + else + { + RTPrintf(GuestCtrl::tr("Waiting failed with %Rrc\n"), vrc); + break; + } + } + else if (pCtx->cVerbose) + { + RTPrintf(GuestCtrl::tr("Run level %RU32 reached\n"), enmRunLevel); + break; + } + + NativeEventQueue::getMainEventQueue()->processEventQueue(0); + } + + if ( vrc == VERR_TIMEOUT + && pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Run level %RU32 not reached within time\n"), enmRunLevel); + } + + if (!pGuestListener.isNull()) + { + /* Guest callback unregistration. */ + ComPtr pES; + CHECK_ERROR(pCtx->pGuest, COMGETTER(EventSource)(pES.asOutParam())); + if (!pES.isNull()) + CHECK_ERROR(pES, UnregisterListener(pGuestListener)); + pGuestListener.setNull(); + } + + if (g_fGuestCtrlCanceled) + vrc = VERR_CANCELLED; + } + catch (std::bad_alloc &) + { + vrc = VERR_NO_MEMORY; + } + + return vrc; +} + +static DECLCALLBACK(RTEXITCODE) gctlHandleUpdateAdditions(PGCTLCMDCTX pCtx, int argc, char **argv) +{ + AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); + + /** Timeout to wait for the whole updating procedure to complete. */ + uint32_t cMsTimeout = RT_INDEFINITE_WAIT; + /** Source path to .ISO Guest Additions file to use. */ + Utf8Str strSource; + com::SafeArray aArgs; + /** Whether to reboot the guest automatically when the update process has finished successfully. */ + bool fRebootOnFinish = false; + /** Whether to only wait for getting the update process started instead of waiting until it finishes. */ + bool fWaitStartOnly = false; + /** Whether to wait for the VM being ready to start the update. Needs Guest Additions facility reporting. */ + bool fWaitReady = false; + /** Whether to verify if the Guest Additions were successfully updated on the guest. */ + bool fVerify = false; + + /* + * Parse arguments. + */ + enum KGSTCTRLUPDATEADDITIONSOPT + { + KGSTCTRLUPDATEADDITIONSOPT_REBOOT = 1000, + KGSTCTRLUPDATEADDITIONSOPT_SOURCE, + KGSTCTRLUPDATEADDITIONSOPT_TIMEOUT, + KGSTCTRLUPDATEADDITIONSOPT_VERIFY, + KGSTCTRLUPDATEADDITIONSOPT_WAITREADY, + KGSTCTRLUPDATEADDITIONSOPT_WAITSTART + }; + + static const RTGETOPTDEF s_aOptions[] = + { + GCTLCMD_COMMON_OPTION_DEFS() + { "--reboot", KGSTCTRLUPDATEADDITIONSOPT_REBOOT, RTGETOPT_REQ_NOTHING }, + { "--source", KGSTCTRLUPDATEADDITIONSOPT_SOURCE, RTGETOPT_REQ_STRING }, + { "--timeout", KGSTCTRLUPDATEADDITIONSOPT_TIMEOUT, RTGETOPT_REQ_UINT32 }, + { "--verify", KGSTCTRLUPDATEADDITIONSOPT_VERIFY, RTGETOPT_REQ_NOTHING }, + { "--wait-ready", KGSTCTRLUPDATEADDITIONSOPT_WAITREADY, RTGETOPT_REQ_NOTHING }, + { "--wait-start", KGSTCTRLUPDATEADDITIONSOPT_WAITSTART, 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 KGSTCTRLUPDATEADDITIONSOPT_REBOOT: + fRebootOnFinish = true; + break; + + case KGSTCTRLUPDATEADDITIONSOPT_SOURCE: + vrc = RTPathAbsCxx(strSource, ValueUnion.psz); + if (RT_FAILURE(vrc)) + return RTMsgErrorExitFailure(GuestCtrl::tr("RTPathAbsCxx failed on '%s': %Rrc"), ValueUnion.psz, vrc); + break; + + case KGSTCTRLUPDATEADDITIONSOPT_WAITSTART: + fWaitStartOnly = true; + break; + + case KGSTCTRLUPDATEADDITIONSOPT_WAITREADY: + fWaitReady = true; + break; + + case KGSTCTRLUPDATEADDITIONSOPT_VERIFY: + fVerify = true; + fRebootOnFinish = true; /* Verification needs a mandatory reboot after successful update. */ + 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 errorGetOpt(ch, &ValueUnion); + } + } + + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Updating Guest Additions ...\n")); + + HRESULT hrc = S_OK; + while (strSource.isEmpty()) + { + ComPtr 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(GuestCtrl::tr("No Guest Additions source found or specified, aborting\n")); + vrc = VERR_FILE_NOT_FOUND; + } + else if (!RTFileExists(strSource.c_str())) + { + RTMsgError(GuestCtrl::tr("Source \"%s\" does not exist!\n"), strSource.c_str()); + vrc = VERR_FILE_NOT_FOUND; + } + + + +#if 0 + ComPtr guest; + HRESULT hrc = pConsole->COMGETTER(Guest)(guest.asOutParam()); + if (SUCCEEDED(hrc) && !guest.isNull()) + { + SHOW_STRING_PROP_NOT_EMPTY(guest, OSTypeId, "GuestOSType", GuestCtrl::tr("OS type:")); + + AdditionsRunLevelType_T guestRunLevel; /** @todo Add a runlevel-to-string (e.g. 0 = "None") method? */ + hrc = guest->COMGETTER(AdditionsRunLevel)(&guestRunLevel); + if (SUCCEEDED(hrc)) + SHOW_ULONG_VALUE("GuestAdditionsRunLevel", GuestCtrl::tr("Additions run level:"), (ULONG)guestRunLevel, ""); + + Bstr guestString; + hrc = guest->COMGETTER(AdditionsVersion)(guestString.asOutParam()); + if ( SUCCEEDED(hrc) + && !guestString.isEmpty()) + { + ULONG uRevision; + hrc = guest->COMGETTER(AdditionsRevision)(&uRevision); + if (FAILED(hrc)) + uRevision = 0; + RTStrPrintf(szValue, sizeof(szValue), "%ls r%u", guestString.raw(), uRevision); + SHOW_UTF8_STRING("GuestAdditionsVersion", GuestCtrl::tr("Additions version:"), szValue); + } + } +#endif + + if (RT_SUCCESS(vrc)) + { + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Using source: %s\n"), strSource.c_str()); + + RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx); + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + + if (fWaitReady) + { + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Waiting for current Guest Additions inside VM getting ready for updating ...\n")); + + const uint64_t uTsStart = RTTimeMilliTS(); + vrc = gctlWaitForRunLevel(pCtx, AdditionsRunLevelType_Userland, cMsTimeout); + if (RT_SUCCESS(vrc)) + cMsTimeout = cMsTimeout != RT_INDEFINITE_WAIT ? cMsTimeout - (RTTimeMilliTS() - uTsStart) : cMsTimeout; + } + + if (RT_SUCCESS(vrc)) + { + /* Get current Guest Additions version / revision. */ + Bstr strGstVerCur; + ULONG uGstRevCur = 0; + hrc = pCtx->pGuest->COMGETTER(AdditionsVersion)(strGstVerCur.asOutParam()); + if ( SUCCEEDED(hrc) + && !strGstVerCur.isEmpty()) + { + hrc = pCtx->pGuest->COMGETTER(AdditionsRevision)(&uGstRevCur); + if (SUCCEEDED(hrc)) + { + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Guest Additions %lsr%RU64 currently installed, waiting for Guest Additions installer to start ...\n"), + strGstVerCur.raw(), uGstRevCur); + } + } + + com::SafeArray aUpdateFlags; + if (fWaitStartOnly) + aUpdateFlags.push_back(AdditionsUpdateFlag_WaitForUpdateStartOnly); + + ComPtr pProgress; + CHECK_ERROR(pCtx->pGuest, UpdateGuestAdditions(Bstr(strSource).raw(), + ComSafeArrayAsInParam(aArgs), + ComSafeArrayAsInParam(aUpdateFlags), + pProgress.asOutParam())); + if (FAILED(hrc)) + vrc = gctlPrintError(pCtx->pGuest, COM_IIDOF(IGuest)); + else + { + if (pCtx->cVerbose) + hrc = showProgress(pProgress); + else + hrc = pProgress->WaitForCompletion((int32_t)cMsTimeout); + + if (SUCCEEDED(hrc)) + CHECK_PROGRESS_ERROR(pProgress, (GuestCtrl::tr("Guest Additions update failed"))); + vrc = gctlPrintProgressError(pProgress); + if (RT_SUCCESS(vrc)) + { + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Guest Additions update successful.\n")); + + if (fRebootOnFinish) + { + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Rebooting guest ...\n")); + com::SafeArray aShutdownFlags; + aShutdownFlags.push_back(GuestShutdownFlag_Reboot); + CHECK_ERROR(pCtx->pGuest, Shutdown(ComSafeArrayAsInParam(aShutdownFlags))); + if (FAILED(hrc)) + { + if (hrc == VBOX_E_NOT_SUPPORTED) + { + RTPrintf(GuestCtrl::tr("Current installed Guest Additions don't support automatic rebooting. " + "Please reboot manually.\n")); + vrc = VERR_NOT_SUPPORTED; + } + else + vrc = gctlPrintError(pCtx->pGuest, COM_IIDOF(IGuest)); + } + else + { + if (fWaitReady) + { + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Waiting for new Guest Additions inside VM getting ready ...\n")); + + vrc = gctlWaitForRunLevel(pCtx, AdditionsRunLevelType_Userland, cMsTimeout); + if (RT_SUCCESS(vrc)) + { + if (fVerify) + { + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Verifying Guest Additions update ...\n")); + + /* Get new Guest Additions version / revision. */ + Bstr strGstVerNew; + ULONG uGstRevNew = 0; + hrc = pCtx->pGuest->COMGETTER(AdditionsVersion)(strGstVerNew.asOutParam()); + if ( SUCCEEDED(hrc) + && !strGstVerNew.isEmpty()) + { + hrc = pCtx->pGuest->COMGETTER(AdditionsRevision)(&uGstRevNew); + if (FAILED(hrc)) + uGstRevNew = 0; + } + + /** @todo Do more verification here. */ + vrc = uGstRevNew > uGstRevCur ? VINF_SUCCESS : VERR_NO_CHANGE; + + if (pCtx->cVerbose) + { + RTPrintf(GuestCtrl::tr("Old Guest Additions: %ls%RU64\n"), strGstVerCur.raw(), + uGstRevCur); + RTPrintf(GuestCtrl::tr("New Guest Additions: %ls%RU64\n"), strGstVerNew.raw(), + uGstRevNew); + + if (RT_FAILURE(vrc)) + { + RTPrintf(GuestCtrl::tr("\nError updating Guest Additions, please check guest installer log\n")); + } + else + { + if (uGstRevNew < uGstRevCur) + RTPrintf(GuestCtrl::tr("\nWARNING: Guest Additions were downgraded\n")); + } + } + } + } + } + else if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("The guest needs to be restarted in order to make use of the updated Guest Additions.\n")); + } + } + } + } + } + } + + return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +/** + * Returns a Guest Additions run level from a string. + * + * @returns Run level if found, or AdditionsRunLevelType_None if not found / invalid. + * @param pcszStr String to return run level for. + */ +static AdditionsRunLevelType_T gctlGetRunLevelFromStr(const char *pcszStr) +{ + AssertPtrReturn(pcszStr, AdditionsRunLevelType_None); + + if (RTStrICmp(pcszStr, "system") == 0) return AdditionsRunLevelType_System; + else if (RTStrICmp(pcszStr, "userland") == 0) return AdditionsRunLevelType_Userland; + else if (RTStrICmp(pcszStr, "desktop") == 0) return AdditionsRunLevelType_Desktop; + + return AdditionsRunLevelType_None; +} + +static DECLCALLBACK(RTEXITCODE) gctlHandleWaitRunLevel(PGCTLCMDCTX pCtx, int argc, char **argv) +{ + AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); + + /** Timeout to wait for run level being reached. + * By default we wait until it's reached. */ + uint32_t cMsTimeout = RT_INDEFINITE_WAIT; + + /* + * Parse arguments. + */ + enum KGSTCTRLWAITRUNLEVELOPT + { + KGSTCTRLWAITRUNLEVELOPT_TIMEOUT = 1000 + }; + + static const RTGETOPTDEF s_aOptions[] = + { + GCTLCMD_COMMON_OPTION_DEFS() + { "--timeout", KGSTCTRLWAITRUNLEVELOPT_TIMEOUT, RTGETOPT_REQ_UINT32 } + }; + + int ch; + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST); + + AdditionsRunLevelType_T enmRunLevel = AdditionsRunLevelType_None; + + int vrc = VINF_SUCCESS; + while ( (ch = RTGetOpt(&GetState, &ValueUnion)) + && RT_SUCCESS(vrc)) + { + switch (ch) + { + GCTLCMD_COMMON_OPTION_CASES(pCtx, ch, &ValueUnion); + + case KGSTCTRLWAITRUNLEVELOPT_TIMEOUT: + cMsTimeout = ValueUnion.u32; + break; + + case VINF_GETOPT_NOT_OPTION: + { + enmRunLevel = gctlGetRunLevelFromStr(ValueUnion.psz); + if (enmRunLevel == AdditionsRunLevelType_None) + return errorSyntax(GuestCtrl::tr("Invalid run level specified. Valid values are: system, userland, desktop")); + break; + } + + default: + return errorGetOpt(ch, &ValueUnion); + } + } + + RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx); + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + + if (enmRunLevel == AdditionsRunLevelType_None) + return errorSyntax(GuestCtrl::tr("Missing run level to wait for")); + + vrc = gctlWaitForRunLevel(pCtx, enmRunLevel, cMsTimeout); + + 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 errorSyntax(GuestCtrl::tr("Unknown list: '%s'"), ValueUnion.psz); + fSeenListArg = true; + break; + + default: + return errorGetOpt(ch, &ValueUnion); + } + } + + if (!fSeenListArg) + return errorSyntax(GuestCtrl::tr("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 hrc; + size_t cTotalProcs = 0; + size_t cTotalFiles = 0; + + SafeIfaceArray collSessions; + CHECK_ERROR(pCtx->pGuest, COMGETTER(Sessions)(ComSafeArrayAsOutParam(collSessions))); + if (SUCCEEDED(hrc)) + { + size_t const cSessions = collSessions.size(); + if (cSessions) + { + RTPrintf(GuestCtrl::tr("Active guest sessions:\n")); + + /** @todo Make this output a bit prettier. No time now. */ + + for (size_t i = 0; i < cSessions; i++) + { + ComPtr 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(GuestCtrl::tr("\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 collProcesses; + CHECK_ERROR_BREAK(pCurSession, COMGETTER(Processes)(ComSafeArrayAsOutParam(collProcesses))); + for (size_t a = 0; a < collProcesses.size(); a++) + { + ComPtr 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(GuestCtrl::tr("\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 collFiles; + CHECK_ERROR_BREAK(pCurSession, COMGETTER(Files)(ComSafeArrayAsOutParam(collFiles))); + for (size_t a = 0; a < collFiles.size(); a++) + { + ComPtr 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(GuestCtrl::tr("\n\t\tFile #%-03zu ID=%-6RU32 Status=[%s] Name=%ls"), + a, idFile, gctlFileStatusToText(fileStatus), strName.raw()); + } while (0); + } + } + + cTotalFiles += collFiles.size(); + } + } + } + + RTPrintf(GuestCtrl::tr("\n\nTotal guest sessions: %zu\n"), collSessions.size()); + if (fListAll || fListProcesses) + RTPrintf(GuestCtrl::tr("Total guest processes: %zu\n"), cTotalProcs); + if (fListAll || fListFiles) + RTPrintf(GuestCtrl::tr("Total guest files: %zu\n"), cTotalFiles); + } + else + RTPrintf(GuestCtrl::tr("No active guest sessions found\n")); + } + + if (FAILED(hrc)) /** @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; + int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + 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; + vrc = RTStrToUInt32Ex(ValueUnion.psz, NULL, 0, &uPid); + if ( RT_SUCCESS(vrc) + && vrc != VWRN_TRAILING_CHARS + && vrc != VWRN_NUMBER_TOO_BIG + && vrc != VWRN_NEGATIVE_UNSIGNED) + { + if (uPid != 0) + { + try + { + vecPID.push_back(uPid); + } + catch (std::bad_alloc &) + { + return RTMsgErrorExit(RTEXITCODE_FAILURE, GuestCtrl::tr("Out of memory")); + } + } + else + return errorSyntax(GuestCtrl::tr("Invalid PID value: 0")); + } + else + return errorSyntax(GuestCtrl::tr("Error parsing PID value: %Rrc"), vrc); + break; + } + + default: + return errorGetOpt(ch, &ValueUnion); + } + } + + if (vecPID.empty()) + return errorSyntax(GuestCtrl::tr("At least one PID must be specified to kill!")); + + if ( strSessionName.isEmpty() + && idSession == UINT32_MAX) + return errorSyntax(GuestCtrl::tr("No session ID specified!")); + + if ( strSessionName.isNotEmpty() + && idSession != UINT32_MAX) + return errorSyntax(GuestCtrl::tr("Either session ID or name (pattern) must be specified")); + + RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx); + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + + HRESULT hrc = S_OK; + + ComPtr pSession; + ComPtr pProcess; + do + { + uint32_t uProcsTerminated = 0; + + SafeIfaceArray 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 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(GuestCtrl::tr("Terminating process (PID %RU32) (session ID %RU32) ...\n"), + uPID, uID); + CHECK_ERROR_BREAK(pProcess, Terminate()); + uProcsTerminated++; + } + else + { + if (idSession != UINT32_MAX) + RTPrintf(GuestCtrl::tr("No matching process(es) for session ID %RU32 found\n"), + idSession); + } + + pProcess.setNull(); + } + + pSession.setNull(); + } + } + + if (!cSessionsHandled) + RTPrintf(GuestCtrl::tr("No matching session(s) found\n")); + + if (uProcsTerminated) + RTPrintf(GuestCtrl::tr("%RU32 process(es) terminated\n", "", uProcsTerminated), uProcsTerminated); + + } while (0); + + pProcess.setNull(); + pSession.setNull(); + + return SUCCEEDED(hrc) ? 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 errorGetOpt(ch, &ValueUnion); + } + } + + if ( strSessionName.isEmpty() + && idSession == UINT32_MAX) + return errorSyntax(GuestCtrl::tr("No session ID specified!")); + + if ( !strSessionName.isEmpty() + && idSession != UINT32_MAX) + return errorSyntax(GuestCtrl::tr("Either session ID or name (pattern) must be specified")); + + RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx); + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + + HRESULT hrc = S_OK; + + do + { + size_t cSessionsHandled = 0; + + SafeIfaceArray collSessions; + CHECK_ERROR_BREAK(pCtx->pGuest, COMGETTER(Sessions)(ComSafeArrayAsOutParam(collSessions))); + size_t cSessions = collSessions.size(); + + for (size_t i = 0; i < cSessions; i++) + { + ComPtr 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(GuestCtrl::tr("Closing guest session ID=#%RU32 \"%s\" ...\n"), + uID, strNameUtf8.c_str()); + CHECK_ERROR_BREAK(pSession, Close()); + if (pCtx->cVerbose) + RTPrintf(GuestCtrl::tr("Guest session successfully closed\n")); + + pSession.setNull(); + } + } + + if (!cSessionsHandled) + { + RTPrintf(GuestCtrl::tr("No guest session(s) found\n")); + hrc = E_ABORT; /* To set exit code accordingly. */ + } + + } while (0); + + return SUCCEEDED(hrc) ? 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() + { "--timeout", 't', RTGETOPT_REQ_UINT32 } + }; + + uint32_t cMsTimeout = RT_INDEFINITE_WAIT; + + 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 't': /* Timeout */ + cMsTimeout = ValueUnion.u32; + break; + + case VINF_GETOPT_NOT_OPTION: + default: + return errorGetOpt(ch, &ValueUnion); + } + } + + /** @todo Specify categories to watch for. */ + + RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx); + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + + HRESULT hrc; + + try + { + ComObjPtr pGuestListener; + do + { + /* Listener creation. */ + pGuestListener.createObject(); + pGuestListener->init(new GuestEventListener()); + + /* Register for IGuest events. */ + ComPtr es; + CHECK_ERROR_BREAK(pCtx->pGuest, COMGETTER(EventSource)(es.asOutParam())); + com::SafeArray 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(GuestCtrl::tr("Waiting for events ...\n")); + + RTMSINTERVAL tsStart = RTTimeMilliTS(); + while (RTTimeMilliTS() - tsStart < cMsTimeout) + { + /* Wait for the global signal semaphore getting signalled. */ + int vrc = RTSemEventWait(g_SemEventGuestCtrlCanceled, 100 /* ms */); + if (RT_FAILURE(vrc)) + { + if (vrc != VERR_TIMEOUT) + { + RTPrintf(GuestCtrl::tr("Waiting failed with %Rrc\n"), vrc); + break; + } + } + else + break; + + /* We need to process the event queue, otherwise our registered listeners won't get any events. */ + NativeEventQueue::getMainEventQueue()->processEventQueue(0); + } + + if (!pGuestListener.isNull()) + { + /* Guest callback unregistration. */ + ComPtr pES; + CHECK_ERROR(pCtx->pGuest, COMGETTER(EventSource)(pES.asOutParam())); + if (!pES.isNull()) + CHECK_ERROR(pES, UnregisterListener(pGuestListener)); + pGuestListener.setNull(); + } + } + catch (std::bad_alloc &) + { + hrc = E_OUTOFMEMORY; + } + + return SUCCEEDED(hrc) ? 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); + + /* + * Command definitions. + */ + static const GCTLCMDDEF s_aCmdDefs[] = + { + { "run", gctlHandleRun, HELP_SCOPE_GUESTCONTROL_RUN, 0 }, + { "start", gctlHandleStart, HELP_SCOPE_GUESTCONTROL_START, 0 }, + { "copyfrom", gctlHandleCopyFrom, HELP_SCOPE_GUESTCONTROL_COPYFROM, 0 }, + { "copyto", gctlHandleCopyTo, HELP_SCOPE_GUESTCONTROL_COPYTO, 0 }, + + { "mkdir", gctrlHandleMkDir, HELP_SCOPE_GUESTCONTROL_MKDIR, 0 }, + { "md", gctrlHandleMkDir, HELP_SCOPE_GUESTCONTROL_MKDIR, 0 }, + { "createdirectory", gctrlHandleMkDir, HELP_SCOPE_GUESTCONTROL_MKDIR, 0 }, + { "createdir", gctrlHandleMkDir, HELP_SCOPE_GUESTCONTROL_MKDIR, 0 }, + + { "rmdir", gctlHandleRmDir, HELP_SCOPE_GUESTCONTROL_RMDIR, 0 }, + { "removedir", gctlHandleRmDir, HELP_SCOPE_GUESTCONTROL_RMDIR, 0 }, + { "removedirectory", gctlHandleRmDir, HELP_SCOPE_GUESTCONTROL_RMDIR, 0 }, + + { "rm", gctlHandleRm, HELP_SCOPE_GUESTCONTROL_RM, 0 }, + { "removefile", gctlHandleRm, HELP_SCOPE_GUESTCONTROL_RM, 0 }, + { "erase", gctlHandleRm, HELP_SCOPE_GUESTCONTROL_RM, 0 }, + { "del", gctlHandleRm, HELP_SCOPE_GUESTCONTROL_RM, 0 }, + { "delete", gctlHandleRm, HELP_SCOPE_GUESTCONTROL_RM, 0 }, + + { "mv", gctlHandleMv, HELP_SCOPE_GUESTCONTROL_MV, 0 }, + { "move", gctlHandleMv, HELP_SCOPE_GUESTCONTROL_MV, 0 }, + { "ren", gctlHandleMv, HELP_SCOPE_GUESTCONTROL_MV, 0 }, + { "rename", gctlHandleMv, HELP_SCOPE_GUESTCONTROL_MV, 0 }, + + { "mktemp", gctlHandleMkTemp, HELP_SCOPE_GUESTCONTROL_MKTEMP, 0 }, + { "createtemp", gctlHandleMkTemp, HELP_SCOPE_GUESTCONTROL_MKTEMP, 0 }, + { "createtemporary", gctlHandleMkTemp, HELP_SCOPE_GUESTCONTROL_MKTEMP, 0 }, + + { "stat", gctlHandleStat, HELP_SCOPE_GUESTCONTROL_STAT, 0 }, + + { "closeprocess", gctlHandleCloseProcess, HELP_SCOPE_GUESTCONTROL_CLOSEPROCESS, GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER }, + { "closesession", gctlHandleCloseSession, HELP_SCOPE_GUESTCONTROL_CLOSESESSION, GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER }, + { "list", gctlHandleList, HELP_SCOPE_GUESTCONTROL_LIST, GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER }, + { "watch", gctlHandleWatch, HELP_SCOPE_GUESTCONTROL_WATCH, GCTLCMDCTX_F_SESSION_ANONYMOUS }, + + {"updateguestadditions",gctlHandleUpdateAdditions, HELP_SCOPE_GUESTCONTROL_UPDATEGA, GCTLCMDCTX_F_SESSION_ANONYMOUS }, + { "updateadditions", gctlHandleUpdateAdditions, HELP_SCOPE_GUESTCONTROL_UPDATEGA, GCTLCMDCTX_F_SESSION_ANONYMOUS }, + { "updatega", gctlHandleUpdateAdditions, HELP_SCOPE_GUESTCONTROL_UPDATEGA, GCTLCMDCTX_F_SESSION_ANONYMOUS }, + + { "waitrunlevel", gctlHandleWaitRunLevel, HELP_SCOPE_GUESTCONTROL_WAITRUNLEVEL, GCTLCMDCTX_F_SESSION_ANONYMOUS }, + { "waitforrunlevel", gctlHandleWaitRunLevel, HELP_SCOPE_GUESTCONTROL_WAITRUNLEVEL, GCTLCMDCTX_F_SESSION_ANONYMOUS }, + }; + + /* + * VBoxManage guestcontrol [common-options] [common-options] ... + * + * 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]; + + setCurrentSubcommand(s_aCmdDefs[iCmd].fSubcommandScope); + rcExit = s_aCmdDefs[iCmd].pfnHandler(&CmdCtx, pArg->argc - GetState.iNext + 1, + &pArg->argv[GetState.iNext - 1]); + + gctlCtxTerm(&CmdCtx); + return rcExit; + } + return errorSyntax(GuestCtrl::tr("Unknown sub-command: '%s'"), pszCmd); + } + break; + + default: + return errorGetOpt(ch, &ValueUnion); + } + } + if (CmdCtx.pszVmNameOrUuid) + rcExit = errorSyntax(GuestCtrl::tr("Missing sub-command")); + else + rcExit = errorSyntax(GuestCtrl::tr("Missing VM name and sub-command")); + } + return rcExit; +} diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.h b/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.h new file mode 100644 index 00000000..c98cad06 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.h @@ -0,0 +1,270 @@ +/* $Id: VBoxManageGuestCtrl.h $ */ +/** @file + * VBoxManageGuestCtrl.h - Definitions for guest control. + */ + +/* + * Copyright (C) 2013-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#ifndef VBOX_INCLUDED_SRC_VBoxManage_VBoxManageGuestCtrl_h +#define VBOX_INCLUDED_SRC_VBoxManage_VBoxManageGuestCtrl_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include +#include +#include + +#include +#include + +#include + +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 GuestFileEventListenerImpl; + +class GuestProcessEventListener; +typedef ListenerImpl GuestProcessEventListenerImpl; + +class GuestSessionEventListener; +typedef ListenerImpl GuestSessionEventListenerImpl; + +class GuestEventListener; +typedef ListenerImpl GuestEventListenerImpl; + +class GuestAdditionsRunlevelListener; +typedef ListenerImpl GuestAdditionsRunlevelListenerImpl; + +/** 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 pListenerImpl) + : mListener(pListenerImpl) + { + } + +public: /** @todo */ + + ComObjPtr mListener; +}; + +class GuestProcStats : public GuestEventStats +{ + +public: + + GuestProcStats(void) { } + + GuestProcStats(ComObjPtr pListenerImpl) + : mListener(pListenerImpl) + { + } + +public: /** @todo */ + + ComObjPtr mListener; +}; + +class GuestSessionStats : public GuestEventStats +{ + +public: + + GuestSessionStats(void) { } + + GuestSessionStats(ComObjPtr pListenerImpl) + : mListener(pListenerImpl) + { + } + +public: /** @todo */ + + ComObjPtr mListener; +}; + +/** Map containing all watched guest files. */ +typedef std::map< ComPtr, GuestFileStats > GuestEventFiles; +/** Map containing all watched guest processes. */ +typedef std::map< ComPtr, GuestProcStats > GuestEventProcs; +/** Map containing all watched guest sessions. */ +typedef std::map< ComPtr, 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; +}; + +/** + * Handler for Guest Additions runlevel change events. + */ +class GuestAdditionsRunlevelListener : public GuestListenerBase +{ + +public: + + GuestAdditionsRunlevelListener(AdditionsRunLevelType_T enmRunLevel); + + virtual ~GuestAdditionsRunlevelListener(void); + +public: + + void uninit(void); + + STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent); + +protected: + + /** The run level target we're waiting for. */ + AdditionsRunLevelType_T mRunLevelTarget; +}; + +#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..0fada6d0 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrlListener.cpp @@ -0,0 +1,578 @@ +/* $Id: VBoxManageGuestCtrlListener.cpp $ */ +/** @file + * VBoxManage - Guest control listener implementations. + */ + +/* + * Copyright (C) 2013-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxManage.h" +#include "VBoxManageGuestCtrl.h" + +#include +#include +#include + +#include +#include + +#include +#include + +DECLARE_TRANSLATION_CONTEXT(GuestCtrlLsnr); + + +/** Event semaphore we're using for notification. */ +extern RTSEMEVENT g_SemEventGuestCtrlCanceled; + + +/* + * 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 hrc; + do + { + ComPtr pEvent = aEvent; + Assert(!pEvent.isNull()); + + ComPtr 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(GuestCtrlLsnr::tr("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 hrc; + do + { + ComPtr pEvent = aEvent; + Assert(!pEvent.isNull()); + + ComPtr 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(GuestCtrlLsnr::tr("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 hrc; + do + { + /* Listener unregistration. */ + ComPtr pES; + CHECK_ERROR_BREAK(itProc->first, COMGETTER(EventSource)(pES.asOutParam())); + if (!pES.isNull()) + CHECK_ERROR_BREAK(pES, UnregisterListener(itProc->second.mListener)); + } while (0); + } + + ++itProc; + } + mProcs.clear(); + + GuestEventFiles::iterator itFile = mFiles.begin(); + while (itFile != mFiles.end()) + { + if (!itFile->first.isNull()) + { + HRESULT hrc; + do + { + /* Listener unregistration. */ + ComPtr pES; + CHECK_ERROR_BREAK(itFile->first, COMGETTER(EventSource)(pES.asOutParam())); + if (!pES.isNull()) + CHECK_ERROR_BREAK(pES, UnregisterListener(itFile->second.mListener)); + } while (0); + } + + ++itFile; + } + mFiles.clear(); +} + +STDMETHODIMP GuestSessionEventListener::HandleEvent(VBoxEventType_T aType, IEvent *aEvent) +{ + switch (aType) + { + case VBoxEventType_OnGuestFileRegistered: + { + HRESULT hrc; + do + { + ComPtr pEvent = aEvent; + Assert(!pEvent.isNull()); + + ComPtr 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(GuestCtrlLsnr::tr("File \"%s\" %s\n"), + Utf8Str(strPath).c_str(), + fRegistered ? GuestCtrlLsnr::tr("registered") : GuestCtrlLsnr::tr("unregistered")); + if (fRegistered) + { + if (mfVerbose) + RTPrintf(GuestCtrlLsnr::tr("Registering ...\n")); + + /* Register for IGuestFile events. */ + ComObjPtr pListener; + pListener.createObject(); + CHECK_ERROR_BREAK(pListener, init(new GuestFileEventListener())); + + ComPtr es; + CHECK_ERROR_BREAK(pFile, COMGETTER(EventSource)(es.asOutParam())); + com::SafeArray 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(GuestCtrlLsnr::tr("Unregistering file ...\n")); + + if (!itFile->first.isNull()) + { + /* Listener unregistration. */ + ComPtr pES; + CHECK_ERROR(itFile->first, COMGETTER(EventSource)(pES.asOutParam())); + if (!pES.isNull()) + CHECK_ERROR(pES, UnregisterListener(itFile->second.mListener)); + } + + mFiles.erase(itFile); + } + } + + } while (0); + break; + } + + case VBoxEventType_OnGuestProcessRegistered: + { + HRESULT hrc; + do + { + ComPtr pEvent = aEvent; + Assert(!pEvent.isNull()); + + ComPtr 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(GuestCtrlLsnr::tr("Process \"%s\" %s\n"), + Utf8Str(strPath).c_str(), + fRegistered ? GuestCtrlLsnr::tr("registered") : GuestCtrlLsnr::tr("unregistered")); + if (fRegistered) + { + if (mfVerbose) + RTPrintf(GuestCtrlLsnr::tr("Registering ...\n")); + + /* Register for IGuestProcess events. */ + ComObjPtr pListener; + pListener.createObject(); + CHECK_ERROR_BREAK(pListener, init(new GuestProcessEventListener())); + + ComPtr es; + CHECK_ERROR_BREAK(pProcess, COMGETTER(EventSource)(es.asOutParam())); + com::SafeArray 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(GuestCtrlLsnr::tr("Unregistering process ...\n")); + + if (!itProc->first.isNull()) + { + /* Listener unregistration. */ + ComPtr pES; + CHECK_ERROR(itProc->first, COMGETTER(EventSource)(pES.asOutParam())); + if (!pES.isNull()) + CHECK_ERROR(pES, UnregisterListener(itProc->second.mListener)); + } + + mProcs.erase(itProc); + } + } + + } while (0); + break; + } + + case VBoxEventType_OnGuestSessionStateChanged: + { + HRESULT hrc; + do + { + ComPtr pEvent = aEvent; + Assert(!pEvent.isNull()); + ComPtr 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(GuestCtrlLsnr::tr("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 hrc; + do + { + /* Listener unregistration. */ + ComPtr pES; + CHECK_ERROR_BREAK(itSession->first, COMGETTER(EventSource)(pES.asOutParam())); + if (!pES.isNull()) + CHECK_ERROR_BREAK(pES, UnregisterListener(itSession->second.mListener)); + + } while (0); + } + + ++itSession; + } + mSessions.clear(); +} + +STDMETHODIMP GuestEventListener::HandleEvent(VBoxEventType_T aType, IEvent *aEvent) +{ + switch (aType) + { + case VBoxEventType_OnGuestSessionRegistered: + { + HRESULT hrc; + do + { + ComPtr pEvent = aEvent; + Assert(!pEvent.isNull()); + + ComPtr 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(GuestCtrlLsnr::tr("Session ID=%RU32 \"%s\" %s\n"), + uID, Utf8Str(strName).c_str(), + fRegistered ? GuestCtrlLsnr::tr("registered") : GuestCtrlLsnr::tr("unregistered")); + if (fRegistered) + { + if (mfVerbose) + RTPrintf(GuestCtrlLsnr::tr("Registering ...\n")); + + /* Register for IGuestSession events. */ + ComObjPtr pListener; + pListener.createObject(); + CHECK_ERROR_BREAK(pListener, init(new GuestSessionEventListener())); + + ComPtr es; + CHECK_ERROR_BREAK(pSession, COMGETTER(EventSource)(es.asOutParam())); + com::SafeArray eventTypes; + eventTypes.push_back(VBoxEventType_OnGuestFileRegistered); + eventTypes.push_back(VBoxEventType_OnGuestProcessRegistered); + eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged); + 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(GuestCtrlLsnr::tr("Unregistering ...\n")); + + if (!itSession->first.isNull()) + { + /* Listener unregistration. */ + ComPtr pES; + CHECK_ERROR_BREAK(itSession->first, COMGETTER(EventSource)(pES.asOutParam())); + if (!pES.isNull()) + CHECK_ERROR_BREAK(pES, UnregisterListener(itSession->second.mListener)); + } + + mSessions.erase(itSession); + } + } + + } while (0); + break; + } + + default: + AssertFailed(); + } + + return S_OK; +} + +/* + * GuestAdditionsRunlevelListener + * GuestAdditionsRunlevelListener + * GuestAdditionsRunlevelListener + */ + +GuestAdditionsRunlevelListener::GuestAdditionsRunlevelListener(AdditionsRunLevelType_T enmRunLevel) + : mRunLevelTarget(enmRunLevel) +{ +} + +GuestAdditionsRunlevelListener::~GuestAdditionsRunlevelListener(void) +{ +} + +void GuestAdditionsRunlevelListener::uninit(void) +{ +} + +STDMETHODIMP GuestAdditionsRunlevelListener::HandleEvent(VBoxEventType_T aType, IEvent *aEvent) +{ + Assert(mRunLevelTarget != AdditionsRunLevelType_None); + + HRESULT hrc; + + switch (aType) + { + case VBoxEventType_OnGuestAdditionsStatusChanged: + { + ComPtr pEvent = aEvent; + Assert(!pEvent.isNull()); + + AdditionsRunLevelType_T RunLevelCur = AdditionsRunLevelType_None; + CHECK_ERROR_BREAK(pEvent, COMGETTER(RunLevel)(&RunLevelCur)); + + if (mfVerbose) + RTPrintf(GuestCtrlLsnr::tr("Reached run level %RU32\n"), RunLevelCur); + + if (RunLevelCur == mRunLevelTarget) + { + int vrc = RTSemEventSignal(g_SemEventGuestCtrlCanceled); + AssertRC(vrc); + } + + break; + } + + default: + AssertFailed(); + } + + return S_OK; +} diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageGuestProp.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageGuestProp.cpp new file mode 100644 index 00000000..bed0edea --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageGuestProp.cpp @@ -0,0 +1,601 @@ +/* $Id: VBoxManageGuestProp.cpp $ */ +/** @file + * VBoxManage - Implementation of guestproperty command. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxManage.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef USE_XPCOM_QUEUE +# include +# include +#endif + +#ifdef RT_OS_DARWIN +# include +#endif + +using namespace com; + +DECLARE_TRANSLATION_CONTEXT(GuestProp); + + +static RTEXITCODE handleGetGuestProperty(HandlerArg *a) +{ + HRESULT hrc = S_OK; + + setCurrentSubcommand(HELP_SCOPE_GUESTPROPERTY_GET); + + 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(GuestProp::tr("Incorrect parameters")); + + ComPtr 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(GuestProp::tr("No value set!\n")); + else + RTPrintf(GuestProp::tr("Value: %ls\n"), value.raw()); + if (!value.isEmpty() && verbose) + { + RTPrintf(GuestProp::tr("Timestamp: %lld\n"), i64Timestamp); + RTPrintf(GuestProp::tr("Flags: %ls\n"), flags.raw()); + } + } + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +static RTEXITCODE handleSetGuestProperty(HandlerArg *a) +{ + HRESULT hrc = S_OK; + + setCurrentSubcommand(HELP_SCOPE_GUESTPROPERTY_SET); + + /* + * 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(GuestProp::tr("Incorrect parameters")); + /* This is always needed. */ + pszName = a->argv[1]; + + ComPtr 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(hrc)) + CHECK_ERROR(machine, SaveSettings()); + + a->session->UnlockMachine(); + } + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +static RTEXITCODE handleDeleteGuestProperty(HandlerArg *a) +{ + HRESULT hrc = S_OK; + + setCurrentSubcommand(HELP_SCOPE_GUESTPROPERTY_UNSET); + + /* + * 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(GuestProp::tr("Incorrect parameters")); + /* This is always needed. */ + pszName = a->argv[1]; + + ComPtr 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(hrc)) + CHECK_ERROR(machine, SaveSettings()); + + a->session->UnlockMachine(); + } + return SUCCEEDED(hrc) ? 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) +{ + setCurrentSubcommand(HELP_SCOPE_GUESTPROPERTY_ENUMERATE); + + /* + * Parse arguments. + * + * The old syntax was a little boinkers. The --patterns argument just + * indicates that the rest of the arguments are options. Sort of like '--'. + * This has been normalized a little now, by accepting patterns w/o a + * preceding --pattern argument via the VINF_GETOPT_NOT_OPTION. + * Though, the first non-option is always the VM name. + */ + static const RTGETOPTDEF s_aOptions[] = + { + { "--old-format", 'o', RTGETOPT_REQ_NOTHING }, + { "--sort", 's', RTGETOPT_REQ_NOTHING }, + { "--unsort", 'u', RTGETOPT_REQ_NOTHING }, + { "--timestamp", 't', RTGETOPT_REQ_NOTHING }, + { "--ts", 't', RTGETOPT_REQ_NOTHING }, + { "--no-timestamp", 'T', RTGETOPT_REQ_NOTHING }, + { "--abs", 'a', RTGETOPT_REQ_NOTHING }, + { "--absolute", 'a', RTGETOPT_REQ_NOTHING }, + { "--rel", 'r', RTGETOPT_REQ_NOTHING }, + { "--relative", 'r', RTGETOPT_REQ_NOTHING }, + { "--no-ts", 'T', RTGETOPT_REQ_NOTHING }, + { "--flags", 'f', RTGETOPT_REQ_NOTHING }, + { "--no-flags", 'F', RTGETOPT_REQ_NOTHING }, + /* unnecessary legacy: */ + { "--patterns", 'p', RTGETOPT_REQ_STRING }, + { "-patterns", 'p', RTGETOPT_REQ_STRING }, + }; + + int ch; + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0); + + const char *pszVmNameOrUuid = NULL; + Utf8Str strPatterns; + bool fSort = true; + bool fNewStyle = true; + bool fTimestamp = true; + bool fAbsTime = true; + bool fFlags = true; + + while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + /* For options that require an argument, ValueUnion has received the value. */ + switch (ch) + { + case VINF_GETOPT_NOT_OPTION: + /* The first one is the VM name. */ + if (!pszVmNameOrUuid) + { + pszVmNameOrUuid = ValueUnion.psz; + break; + } + /* Everything else would be patterns by the new syntax. */ + RT_FALL_THROUGH(); + case 'p': + if (strPatterns.isNotEmpty()) + if (RT_FAILURE(strPatterns.appendNoThrow(','))) + return RTMsgErrorExitFailure("out of memory!"); + if (RT_FAILURE(strPatterns.appendNoThrow(ValueUnion.psz))) + return RTMsgErrorExitFailure("out of memory!"); + break; + + case 'o': + fNewStyle = false; + break; + + case 's': + fSort = true; + break; + case 'u': + fSort = false; + break; + + case 't': + fTimestamp = true; + break; + case 'T': + fTimestamp = false; + break; + + case 'a': + fAbsTime = true; + break; + case 'r': + fAbsTime = false; + break; + + case 'f': + fFlags = true; + break; + case 'F': + fFlags = false; + break; + + default: + return errorGetOpt(ch, &ValueUnion); + } + } + + /* Only the VM name is required. */ + if (!pszVmNameOrUuid) + return errorSyntax(GuestProp::tr("No VM name or UUID was specified")); + + /* + * Make the actual call to Main. + */ + ComPtr machine; + CHECK_ERROR2I_RET(a->virtualBox, FindMachine(Bstr(pszVmNameOrUuid).raw(), machine.asOutParam()), RTEXITCODE_FAILURE); + + /* open a session for the VM - new or existing */ + CHECK_ERROR2I_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE); + + /* get the mutable session machine */ + a->session->COMGETTER(Machine)(machine.asOutParam()); + + com::SafeArray names; + com::SafeArray values; + com::SafeArray timestamps; + com::SafeArray flags; + CHECK_ERROR2I_RET(machine, EnumerateGuestProperties(Bstr(strPatterns).raw(), + ComSafeArrayAsOutParam(names), + ComSafeArrayAsOutParam(values), + ComSafeArrayAsOutParam(timestamps), + ComSafeArrayAsOutParam(flags)), + RTEXITCODE_FAILURE); + + size_t const cEntries = names.size(); + if (cEntries == 0) + RTPrintf(GuestProp::tr("No properties found.\n")); + else + { + /* Whether we sort it or not, we work it via a indirect index: */ + size_t *paidxSorted = (size_t *)RTMemAlloc(sizeof(paidxSorted[0]) * cEntries); + if (!paidxSorted) + return RTMsgErrorExitFailure("out of memory!"); + for (size_t i = 0; i < cEntries; i++) + paidxSorted[i] = i; + + /* Do the sorting: */ + if (fSort && cEntries > 1) + for (size_t i = 0; i < cEntries - 1; i++) + for (size_t j = 0; j < cEntries - i - 1; j++) + if (RTUtf16Cmp(names[paidxSorted[j]], names[paidxSorted[j + 1]]) > 0) + { + size_t iTmp = paidxSorted[j]; + paidxSorted[j] = paidxSorted[j + 1]; + paidxSorted[j + 1] = iTmp; + } + + if (fNewStyle) + { + /* figure the width of the main columns: */ + size_t cwcMaxName = 1; + size_t cwcMaxValue = 1; + for (size_t i = 0; i < cEntries; ++i) + { + size_t cwcName = RTUtf16Len(names[i]); + cwcMaxName = RT_MAX(cwcMaxName, cwcName); + size_t cwcValue = RTUtf16Len(values[i]); + cwcMaxValue = RT_MAX(cwcMaxValue, cwcValue); + } + cwcMaxName = RT_MIN(cwcMaxName, 48); + cwcMaxValue = RT_MIN(cwcMaxValue, 28); + + /* Get the current time for relative time formatting: */ + RTTIMESPEC Now; + RTTimeNow(&Now); + + /* Print the table: */ + for (size_t iSorted = 0; iSorted < cEntries; ++iSorted) + { + size_t const i = paidxSorted[iSorted]; + char szTime[80]; + if (fTimestamp) + { + RTTIMESPEC TimestampTS; + RTTimeSpecSetNano(&TimestampTS, timestamps[i]); + if (fAbsTime) + { + RTTIME Timestamp; + RTTimeToStringEx(RTTimeExplode(&Timestamp, &TimestampTS), &szTime[2], sizeof(szTime) - 2, 3); + } + else + { + RTTIMESPEC DurationTS = Now; + RTTimeFormatDurationEx(&szTime[2], sizeof(szTime) - 2, RTTimeSpecSub(&DurationTS, &TimestampTS), 3); + } + szTime[0] = '@'; + szTime[1] = ' '; + } + else + szTime[0] = '\0'; + + static RTUTF16 s_wszEmpty[] = { 0 }; + PCRTUTF16 const pwszFlags = fFlags ? flags[i] : s_wszEmpty; + + int cchOut = RTPrintf("%-*ls = '%ls'", cwcMaxName, names[i], values[i]); + if (fTimestamp || *pwszFlags) + { + size_t const cwcWidth = cwcMaxName + cwcMaxValue + 6; + size_t const cwcValPadding = (unsigned)cchOut < cwcWidth ? cwcWidth - (unsigned)cchOut : 1; + RTPrintf("%*s%s%s%ls\n", cwcValPadding, "", szTime, *pwszFlags ? " " : "", pwszFlags); + } + else + RTPrintf("\n"); + } + } + else + for (size_t iSorted = 0; iSorted < cEntries; ++iSorted) + { + size_t const i = paidxSorted[iSorted]; + RTPrintf(GuestProp::tr("Name: %ls, value: %ls, timestamp: %lld, flags: %ls\n"), + names[i], values[i], timestamps[i], flags[i]); + } + RTMemFree(paidxSorted); + } + + return RTEXITCODE_SUCCESS; +} + +/** + * 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) +{ + setCurrentSubcommand(HELP_SCOPE_GUESTPROPERTY_WAIT); + + /* + * 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 machine; + HRESULT hrc; + 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(GuestProp::tr("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 es; + CHECK_ERROR(a->virtualBox, COMGETTER(EventSource)(es.asOutParam())); + ComPtr listener; + CHECK_ERROR(es, CreateListener(listener.asOutParam())); + com::SafeArray 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 ev; + hrc = es->GetEvent(listener, cMsWait, ev.asOutParam()); + if (ev) /** @todo r=andy Why not using SUCCEEDED(hrc) here? */ + { + VBoxEventType_T aType; + hrc = ev->COMGETTER(Type)(&aType); + switch (aType) + { + case VBoxEventType_OnGuestPropertyChanged: + { + ComPtr 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; + BOOL aNextWasDeleted; + gpcev->COMGETTER(Value)(aNextValue.asOutParam()); + gpcev->COMGETTER(Flags)(aNextFlags.asOutParam()); + gpcev->COMGETTER(FWasDeleted)(&aNextWasDeleted); + if (aNextWasDeleted) + RTPrintf(GuestProp::tr("Property %ls was deleted\n"), aNextName.raw()); + else + RTPrintf(GuestProp::tr("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(GuestProp::tr("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) +{ + if (a->argc == 0) + return errorNoSubcommand(); + + /** @todo This command does not follow the syntax where the + * comes between the command and subcommand. The commands controlvm, + * snapshot and debugvm puts it between. + */ + + const char * const pszSubCmd = a->argv[0]; + a->argc -= 1; + a->argv += 1; + + /* switch (cmd) */ + if (strcmp(pszSubCmd, "get") == 0) + return handleGetGuestProperty(a); + if (strcmp(pszSubCmd, "set") == 0) + return handleSetGuestProperty(a); + if (strcmp(pszSubCmd, "delete") == 0 || strcmp(pszSubCmd, "unset") == 0) + return handleDeleteGuestProperty(a); + if (strcmp(pszSubCmd, "enumerate") == 0 || strcmp(pszSubCmd, "enum") == 0) + return handleEnumGuestProperty(a); + if (strcmp(pszSubCmd, "wait") == 0) + return handleWaitGuestProperty(a); + + /* default: */ + return errorUnknownSubcommand(pszSubCmd); +} diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp new file mode 100644 index 00000000..0b6f39b1 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp @@ -0,0 +1,513 @@ +/* $Id: VBoxManageHelp.cpp $ */ +/** @file + * VBoxManage - help and other message output. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 * +*********************************************************************************************************************************/ +DECLARE_TRANSLATION_CONTEXT(Help); + +static enum HELP_CMD_VBOXMANAGE g_enmCurCommand = HELP_CMD_COMMON; +/** The scope mask for the current subcommand. */ +static 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_COMMON); + 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; +} + + +/** + * Takes first char and make it uppercase. + * + * @returns pointer to string starting from next char. + * @param pszSrc Source string. + * @param pszDst Pointer to buffer to place first char uppercase. + */ +static const char *captialize(const char *pszSrc, char *pszDst) +{ + *RTStrPutCp(pszDst, RTUniCpToUpper(RTStrGetCp(pszSrc))) = '\0'; + return RTStrNextCp(pszSrc); +} + + +/** + * 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) +{ + /* + * Try to find translated, falling back untranslated. + */ + uint32_t cLinesWritten = 0; + uint32_t cPendingBlankLines = 0; + uint32_t cFound = 0; + PCHELP_LANG_ENTRY_T const apHelpLangEntries[] = + { + ASMAtomicUoReadPtrT(&g_pHelpLangEntry, PCHELP_LANG_ENTRY_T), +#ifdef VBOX_WITH_VBOXMANAGE_NLS + &g_aHelpLangEntries[0] +#endif + }; + for (uint32_t k = 0; k < RT_ELEMENTS(apHelpLangEntries) && cFound == 0; k++) + { + /* skip if english is used */ + if (k > 0 && apHelpLangEntries[k] == apHelpLangEntries[0]) + break; + uint32_t const cHelpEntries = *apHelpLangEntries[k]->pcHelpEntries; + for (uint32_t i = 0; i < cHelpEntries; i++) + { + PCRTMSGREFENTRY pHelp = apHelpLangEntries[k]->papHelpEntries[i]; + if ( pHelp->idInternal == (int64_t)enmCommand + || enmCommand == HELP_CMD_COMMON) + { + cFound++; + if (cFound == 1) + { + if (fSubcommandScope == RTMSGREFENTRYSTR_SCOPE_GLOBAL) + { + char szFirstChar[8]; + RTStrmPrintf(pStrm, Help::tr("Usage - %s%s:\n"), szFirstChar, captialize(pHelp->pszBrief, szFirstChar)); + } + else + RTStrmPrintf(pStrm, Help::tr("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) +{ + /* Try to find translated, then untranslated */ + uint32_t cPendingBlankLines = 0; + uint32_t cFound = 0; + PCHELP_LANG_ENTRY_T const apHelpLangEntries[] = + { + ASMAtomicUoReadPtrT(&g_pHelpLangEntry, PCHELP_LANG_ENTRY_T), +#ifdef VBOX_WITH_VBOXMANAGE_NLS + &g_aHelpLangEntries[0] +#endif + }; + for (uint32_t k = 0; k < RT_ELEMENTS(apHelpLangEntries) && cFound == 0; k++) + { + /* skip if english is used */ + if (k > 0 && apHelpLangEntries[k] == apHelpLangEntries[0]) + break; + uint32_t const cHelpEntries = *apHelpLangEntries[k]->pcHelpEntries; + for (uint32_t i = 0; i < cHelpEntries; i++) + { + PCRTMSGREFENTRY pHelp = apHelpLangEntries[k]->papHelpEntries[i]; + + if ( pHelp->idInternal == (int64_t)enmCommand + || enmCommand == HELP_CMD_COMMON) + { + 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(Help::tr("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(Help::tr("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(Help::tr("Too many parameters")); +} + + +/** + * Display current (sub)command usage and the custom error message. + * + * @returns RTEXITCODE_SYNTAX. + * @param pszFormat Custom error message format string. + * @param va Format arguments. + */ +RTEXITCODE errorSyntaxV(const char *pszFormat, va_list va) +{ + Assert(g_enmCurCommand != HELP_CMD_VBOXMANAGE_INVALID); + + showLogo(g_pStdErr); + + va_list vaCopy; + va_copy(vaCopy, va); + RTMsgErrorV(pszFormat, vaCopy); + va_end(vaCopy); + + 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'); + RTMsgErrorV(pszFormat, va); + } + return RTEXITCODE_SYNTAX; +} + + +/** + * 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, ...) +{ + va_list va; + va_start(va, pszFormat); + RTEXITCODE rcExit = errorSyntaxV(pszFormat, va); + va_end(va); + return rcExit; +} + + +/** + * Display current (sub)command usage and the custom error message. + * + * @returns E_INVALIDARG + * @param pszFormat Custom error message format string. + * @param ... Format arguments. + */ +HRESULT errorSyntaxHr(const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + errorSyntaxV(pszFormat, va); + va_end(va); + return E_INVALIDARG; +} + + +/** + * 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; +} + + +/** + * Print an error message without the syntax stuff. + * + * @returns E_INVALIDARG. + */ +HRESULT errorArgumentHr(const char *pszFormat, ...) +{ + va_list args; + va_start(args, pszFormat); + RTMsgErrorV(pszFormat, args); + va_end(args); + return E_INVALIDARG; +} + + +/** + * 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(Help::tr("Invalid parameter '%s'"), pValueUnion->psz); + else if (rcGetOpt > 0) + { + if (RT_C_IS_PRINT(rcGetOpt)) + RTMsgError(Help::tr("Invalid option -%c"), rcGetOpt); + else + RTMsgError(Help::tr("Invalid option case %i"), rcGetOpt); + } + else if (rcGetOpt == VERR_GETOPT_UNKNOWN_OPTION) + RTMsgError(Help::tr("Unknown option: %s"), pValueUnion->psz); + else if (rcGetOpt == VERR_GETOPT_INVALID_ARGUMENT_FORMAT) + RTMsgError(Help::tr("Invalid argument format: %s"), pValueUnion->psz); + else if (pValueUnion->pDef) + RTMsgError("%s: %Rrs", pValueUnion->pDef->pszLong, rcGetOpt); + else + RTMsgError("%Rrs", rcGetOpt); +} + + +/** + * For use to deal with RTGetOptFetchValue failures. + * + * @retval RTEXITCODE_SYNTAX + * @param iValueNo The value number being fetched, counting the + * RTGetOpt value as zero and the first + * RTGetOptFetchValue call as one. + * @param pszOption The option being parsed. + * @param rcGetOptFetchValue The status returned by RTGetOptFetchValue. + * @param pValueUnion The value union returned by the fetch. + */ +RTEXITCODE errorFetchValue(int iValueNo, const char *pszOption, int rcGetOptFetchValue, union RTGETOPTUNION const *pValueUnion) +{ + Assert(g_enmCurCommand != HELP_CMD_VBOXMANAGE_INVALID); + showLogo(g_pStdErr); + if (rcGetOptFetchValue == VERR_GETOPT_REQUIRED_ARGUMENT_MISSING) + RTMsgError(Help::tr("Missing the %u%s value for option %s"), + iValueNo, + iValueNo == 1 ? Help::tr("st") + : iValueNo == 2 ? Help::tr("nd") + : iValueNo == 3 ? Help::tr("rd") + : Help::tr("th"), + pszOption); + else + errorGetOptWorker(rcGetOptFetchValue, pValueUnion); + return RTEXITCODE_SYNTAX; + +} + + +/** + * 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; +} + + +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" + "Copyright (C) 2005-" VBOX_C_YEAR " " VBOX_VENDOR "\n\n"); + s_fShown = true; + } +} diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageHostonly.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageHostonly.cpp new file mode 100644 index 00000000..d5b2444d --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageHostonly.cpp @@ -0,0 +1,546 @@ +/* $Id: VBoxManageHostonly.cpp $ */ +/** @file + * VBoxManage - Implementation of hostonlyif command. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "VBoxManage.h" + +DECLARE_TRANSLATION_CONTEXT(HostOnly); + + +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(c, &ValueUnion); + } + } + + /* + * Do the work. + */ + ComPtr host; + CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(Host)(host.asOutParam()), RTEXITCODE_FAILURE); + + ComPtr hif; + ComPtr progress; + + CHECK_ERROR2I_RET(host, CreateHostOnlyNetworkInterface(hif.asOutParam(), progress.asOutParam()), RTEXITCODE_FAILURE); + + if (fMachineReadable) + { + progress->WaitForCompletion(10000); /* Ten seconds should probably be enough. */ + CHECK_PROGRESS_ERROR_RET(progress, (""), RTEXITCODE_FAILURE); + } + else + { + /*HRESULT hrc =*/ showProgress(progress); + CHECK_PROGRESS_ERROR_RET(progress, (HostOnly::tr("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(HostOnly::tr("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(HostOnly::tr("Only one interface name can be specified")); + pszName = ValueUnion.psz; + break; + + default: + return errorGetOpt(ch, &ValueUnion); + } + if (!pszName) + return errorSyntax(HostOnly::tr("No interface name was specified")); + + /* + * Do the work. + */ + ComPtr host; + CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(Host)(host.asOutParam()), RTEXITCODE_FAILURE); + + ComPtr 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 progress; + CHECK_ERROR2I_RET(host, RemoveHostOnlyNetworkInterface(guid.raw(), progress.asOutParam()), RTEXITCODE_FAILURE); + + /*HRESULT hrc =*/ showProgress(progress); + CHECK_PROGRESS_ERROR_RET(progress, (HostOnly::tr("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(HostOnly::tr("The --ip option is specified more than once")); + pszIp = ValueUnion.psz; + break; + case 'm': // --netmask + if (pszNetmask) + RTMsgWarning(HostOnly::tr("The --netmask option is specified more than once")); + pszNetmask = ValueUnion.psz; + break; + case 'b': // --ipv6 + if (pszIpv6) + RTMsgWarning(HostOnly::tr("The --ipv6 option is specified more than once")); + pszIpv6 = ValueUnion.psz; + break; + case 'l': // --netmasklengthv6 + if (fNetmasklengthv6) + RTMsgWarning(HostOnly::tr("The --netmasklengthv6 option is specified more than once")); + fNetmasklengthv6 = true; + uNetmasklengthv6 = ValueUnion.u8; + break; + case VINF_GETOPT_NOT_OPTION: + if (pszName) + return errorSyntax(HostOnly::tr("Only one interface name can be specified")); + pszName = ValueUnion.psz; + break; + default: + return errorGetOpt(c, &ValueUnion); + } + } + + /* parameter sanity check */ + if (fDhcp && (fNetmasklengthv6 || pszIpv6 || pszIp || pszNetmask)) + return errorSyntax(HostOnly::tr("You can not use --dhcp with static ip configuration parameters: --ip, --netmask, --ipv6 and --netmasklengthv6.")); + if ((pszIp || pszNetmask) && (fNetmasklengthv6 || pszIpv6)) + return errorSyntax(HostOnly::tr("You can not use ipv4 configuration (--ip and --netmask) with ipv6 (--ipv6 and --netmasklengthv6) simultaneously.")); + + ComPtr host; + CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(Host)(host.asOutParam()), RTEXITCODE_FAILURE); + + ComPtr hif; + CHECK_ERROR2I_RET(host, FindHostNetworkInterfaceByName(Bstr(pszName).raw(), hif.asOutParam()), RTEXITCODE_FAILURE); + if (hif.isNull()) + return errorArgument(HostOnly::tr("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(HostOnly::tr("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(HostOnly::tr("Neither -dhcp nor -ip nor -ipv6 was specfified")); + + return RTEXITCODE_SUCCESS; +} + + +RTEXITCODE handleHostonlyIf(HandlerArg *a) +{ + if (a->argc < 1) + return errorSyntax(HostOnly::tr("No sub-command specified")); + + RTEXITCODE rcExit; + if (!strcmp(a->argv[0], "ipconfig")) + { + setCurrentSubcommand(HELP_SCOPE_HOSTONLYIF_IPCONFIG); + rcExit = handleIpConfig(a); + } +#if defined(VBOX_WITH_NETFLT) && !defined(RT_OS_SOLARIS) + else if (!strcmp(a->argv[0], "create")) + { + setCurrentSubcommand(HELP_SCOPE_HOSTONLYIF_CREATE); + rcExit = handleCreate(a); + } + else if (!strcmp(a->argv[0], "remove")) + { + setCurrentSubcommand(HELP_SCOPE_HOSTONLYIF_REMOVE); + rcExit = handleRemove(a); + } +#endif + else + rcExit = errorSyntax(HostOnly::tr("Unknown sub-command '%s'"), a->argv[0]); + return rcExit; +} + +#ifdef VBOX_WITH_VMNET +struct HostOnlyNetworkOptions +{ + bool fEnable; + bool fDisable; + Bstr bstrNetworkId; + Bstr bstrNetworkName; + Bstr bstrNetworkMask; + Bstr bstrLowerIp; + Bstr bstrUpperIp; + /* Initialize fEnable and fDisable */ + HostOnlyNetworkOptions() : fEnable(false), fDisable(false) {}; +}; +typedef struct HostOnlyNetworkOptions HOSTONLYNETOPT; + +static RTEXITCODE createUpdateHostOnlyNetworkParse(HandlerArg *a, HOSTONLYNETOPT& options) +{ + static const RTGETOPTDEF s_aOptions[] = + { + { "--id", 'i', RTGETOPT_REQ_STRING }, + { "--name", 'n', RTGETOPT_REQ_STRING }, + { "--netmask", 'm', RTGETOPT_REQ_STRING }, + { "--lower-ip", 'l', RTGETOPT_REQ_STRING }, + { "--lowerip", 'l', RTGETOPT_REQ_STRING }, + { "--upper-ip", 'u', RTGETOPT_REQ_STRING }, + { "--upperip", 'u', RTGETOPT_REQ_STRING }, + { "--enable", 'e', RTGETOPT_REQ_NOTHING }, + { "--disable", 'd', RTGETOPT_REQ_NOTHING }, + }; + + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1 /* iFirst */, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'i': + options.bstrNetworkId = ValueUnion.psz; + break; + case 'n': + options.bstrNetworkName = ValueUnion.psz; + break; + case 'm': + options.bstrNetworkMask = ValueUnion.psz; + break; + case 'l': + options.bstrLowerIp = ValueUnion.psz; + break; + case 'u': + options.bstrUpperIp = ValueUnion.psz; + break; + case 'e': + options.fEnable = true; + break; + case 'd': + options.fDisable = true; + break; + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + default: + return errorGetOpt(c, &ValueUnion); + } + } + return RTEXITCODE_SUCCESS; +} + +static RTEXITCODE createUpdateHostOnlyNetworkCommon(ComPtr hostOnlyNetwork, HOSTONLYNETOPT& options) +{ + HRESULT hrc = S_OK; + + if (options.bstrNetworkId.isNotEmpty()) + { + CHECK_ERROR2_RET(hrc, hostOnlyNetwork, COMSETTER(Id)(options.bstrNetworkId.raw()), RTEXITCODE_FAILURE); + } + if (options.bstrNetworkName.isNotEmpty()) + { + CHECK_ERROR2_RET(hrc, hostOnlyNetwork, COMSETTER(NetworkName)(options.bstrNetworkName.raw()), RTEXITCODE_FAILURE); + } + if (options.bstrNetworkMask.isNotEmpty()) + { + CHECK_ERROR2_RET(hrc, hostOnlyNetwork, COMSETTER(NetworkMask)(options.bstrNetworkMask.raw()), RTEXITCODE_FAILURE); + } + if (options.bstrLowerIp.isNotEmpty()) + { + CHECK_ERROR2_RET(hrc, hostOnlyNetwork, COMSETTER(LowerIP)(options.bstrLowerIp.raw()), RTEXITCODE_FAILURE); + } + if (options.bstrUpperIp.isNotEmpty()) + { + CHECK_ERROR2_RET(hrc, hostOnlyNetwork, COMSETTER(UpperIP)(options.bstrUpperIp.raw()), RTEXITCODE_FAILURE); + } + if (options.fEnable) + { + CHECK_ERROR2_RET(hrc, hostOnlyNetwork, COMSETTER(Enabled)(TRUE), RTEXITCODE_FAILURE); + } + if (options.fDisable) + { + CHECK_ERROR2_RET(hrc, hostOnlyNetwork, COMSETTER(Enabled)(FALSE), RTEXITCODE_FAILURE); + } + + return RTEXITCODE_SUCCESS; +} + +static RTEXITCODE handleNetAdd(HandlerArg *a) +{ + HRESULT hrc = S_OK; + + HOSTONLYNETOPT options; + hrc = createUpdateHostOnlyNetworkParse(a, options); + + ComPtr pVirtualBox = a->virtualBox; + ComPtr hostOnlyNetwork; + + if (options.bstrNetworkName.isEmpty()) + return errorArgument(HostOnly::tr("The --name parameter must be specified")); + if (options.bstrNetworkMask.isEmpty()) + return errorArgument(HostOnly::tr("The --netmask parameter must be specified")); + if (options.bstrLowerIp.isEmpty()) + return errorArgument(HostOnly::tr("The --lower-ip parameter must be specified")); + if (options.bstrUpperIp.isEmpty()) + return errorArgument(HostOnly::tr("The --upper-ip parameter must be specified")); + + CHECK_ERROR2_RET(hrc, pVirtualBox, + CreateHostOnlyNetwork(options.bstrNetworkName.raw(), hostOnlyNetwork.asOutParam()), + RTEXITCODE_FAILURE); + return createUpdateHostOnlyNetworkCommon(hostOnlyNetwork, options); +} + +static RTEXITCODE handleNetModify(HandlerArg *a) +{ + HRESULT hrc = S_OK; + + HOSTONLYNETOPT options; + hrc = createUpdateHostOnlyNetworkParse(a, options); + + ComPtr pVirtualBox = a->virtualBox; + ComPtr hostOnlyNetwork; + + if (options.bstrNetworkName.isNotEmpty()) + { + CHECK_ERROR2_RET(hrc, pVirtualBox, + FindHostOnlyNetworkByName(options.bstrNetworkName.raw(), hostOnlyNetwork.asOutParam()), + RTEXITCODE_FAILURE); + } + else if (options.bstrNetworkId.isNotEmpty()) + { + CHECK_ERROR2_RET(hrc, pVirtualBox, + FindHostOnlyNetworkById(options.bstrNetworkId.raw(), hostOnlyNetwork.asOutParam()), + RTEXITCODE_FAILURE); + } + else + return errorArgument(HostOnly::tr("Either --name or --id parameter must be specified")); + + return createUpdateHostOnlyNetworkCommon(hostOnlyNetwork, options); +} + +static RTEXITCODE handleNetRemove(HandlerArg *a) +{ + HRESULT hrc = S_OK; + + static const RTGETOPTDEF s_aOptions[] = + { + { "--id", 'i', RTGETOPT_REQ_STRING }, + { "--name", 'n', RTGETOPT_REQ_STRING }, + }; + + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1 /* iFirst */, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + Bstr strNetworkId, strNetworkName; + + int c; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'i': + strNetworkId=ValueUnion.psz; + break; + case 'n': + strNetworkName=ValueUnion.psz; + break; + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + default: + return errorGetOpt(c, &ValueUnion); + } + } + + ComPtr pVirtualBox = a->virtualBox; + ComPtr hostOnlyNetwork; + + if (!strNetworkName.isEmpty()) + { + CHECK_ERROR2_RET(hrc, pVirtualBox, + FindHostOnlyNetworkByName(strNetworkName.raw(), hostOnlyNetwork.asOutParam()), + RTEXITCODE_FAILURE); + } + else if (!strNetworkId.isEmpty()) + { + CHECK_ERROR2_RET(hrc, pVirtualBox, + FindHostOnlyNetworkById(strNetworkId.raw(), hostOnlyNetwork.asOutParam()), + RTEXITCODE_FAILURE); + } + else + return errorArgument(HostOnly::tr("Either --name or --id parameter must be specified")); + + CHECK_ERROR2_RET(hrc, pVirtualBox, + RemoveHostOnlyNetwork(hostOnlyNetwork), + RTEXITCODE_FAILURE); + return RTEXITCODE_SUCCESS; +} + +RTEXITCODE handleHostonlyNet(HandlerArg *a) +{ + if (a->argc < 1) + return errorSyntax(HostOnly::tr("No sub-command specified")); + + RTEXITCODE rcExit; + if (!strcmp(a->argv[0], "add")) + { + setCurrentSubcommand(HELP_SCOPE_HOSTONLYNET_ADD); + rcExit = handleNetAdd(a); + } + else if (!strcmp(a->argv[0], "modify")) + { + setCurrentSubcommand(HELP_SCOPE_HOSTONLYNET_MODIFY); + rcExit = handleNetModify(a); + } + else if (!strcmp(a->argv[0], "remove")) + { + setCurrentSubcommand(HELP_SCOPE_HOSTONLYNET_REMOVE); + rcExit = handleNetRemove(a); + } + else + rcExit = errorSyntax(HostOnly::tr("Unknown sub-command '%s'"), a->argv[0]); + return rcExit; +} +#endif /* VBOX_WITH_VMNET */ diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp new file mode 100644 index 00000000..524c1be2 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp @@ -0,0 +1,3231 @@ +/* $Id: VBoxManageInfo.cpp $ */ +/** @file + * VBoxManage - The 'showvminfo' command and helper routines. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include +#include +#include + +#include + +#ifdef VBOX_WITH_PCI_PASSTHROUGH +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "VBoxManage.h" +#include "VBoxManageUtils.h" + +using namespace com; + +DECLARE_TRANSLATION_CONTEXT(Info); + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#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 \ + { \ + Assert(a_pszHuman[strlen(a_pszHuman) - 1] == ':'); \ + 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, Info::tr("enabled"), Info::tr("disabled")) + +#define SHOW_ULONG_VALUE(a_pszMachine, a_pszHuman, a_uValue, a_pszUnit) \ + do \ + { \ + Assert(a_pszHuman[strlen(a_pszHuman) - 1] == ':'); \ + 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 \ + { \ + Assert(a_pszHuman[strlen(a_pszHuman) - 1] == ':'); \ + 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, Info::tr("enabled"), Info::tr("disabled")) + +#define SHOW_BOOLEAN_PROP_EX(a_pObj, a_Prop, a_pszMachine, a_pszHuman, a_szTrue, a_szFalse) \ + do \ + { \ + Assert(a_pszHuman[strlen(a_pszHuman) - 1] == ':'); \ + 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 \ + { \ + Assert(a_pszHuman[strlen(a_pszHuman) - 1] == ':'); \ + 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 ? Info::tr("enabled") : Info::tr("disabled")); \ + } while (0) + +#define SHOW_STRING_PROP(a_pObj, a_Prop, a_pszMachine, a_pszHuman) \ + do \ + { \ + Assert(a_pszHuman[strlen(a_pszHuman) - 1] == ':'); \ + 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 \ + { \ + Assert(a_pszHuman[strlen(a_pszHuman) - 1] == ':'); \ + 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 \ + { \ + Assert(a_pszHuman[strlen(a_pszHuman) - 1] == ':'); \ + 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 \ + { \ + Assert(a_pszHuman[strlen(a_pszHuman) - 1] == ':'); \ + SafeArray 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 \ + { \ + Assert(a_pszHuman[strlen(a_pszHuman) - 1] == ':'); \ + 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 \ + { \ + Assert(a_pszHuman[strlen(a_pszHuman) - 1] == ':'); \ + 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 \ + { \ + Assert(a_pszHuman[strlen(a_pszHuman) - 1] == ':'); \ + 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) + + +// 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 &rootSnapshot, + ComPtr ¤tSnapshot, + 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(Info::tr(" %sName: %ls (UUID: %s)%s\n"), + prefix.c_str(), + name.raw(), + Utf8Str(uuid).c_str(), + (fCurrent) ? " *" : ""); + if (!description.isEmpty() && RTUtf16Chr(description.raw(), '\n') == NULL) + RTPrintf(Info::tr(" %sDescription: %ls\n"), prefix.c_str(), description.raw()); + else if (!description.isEmpty()) + RTPrintf(Info::tr(" %sDescription:\n%ls\n"), prefix.c_str(), description.raw()); + } + + /* get the children */ + HRESULT hrc = S_OK; + SafeIfaceArray coll; + CHECK_ERROR2I_RET(rootSnapshot,COMGETTER(Children)(ComSafeArrayAsOutParam(coll)), hrcCheck); + if (!coll.isNull()) + { + for (size_t index = 0; index < coll.size(); ++index) + { + ComPtr snapshot = coll[index]; + if (snapshot) + { + Utf8Str newPrefix; + if (details == VMINFO_MACHINEREADABLE) + newPrefix.printf("%s-%d", prefix.c_str(), index + 1); + else + newPrefix.printf("%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" : Info::tr("powered off"); + case MachineState_Saved: + return fShort ? "saved" : Info::tr("saved"); + case MachineState_Teleported: + return fShort ? "teleported" : Info::tr("teleported"); + case MachineState_Aborted: + return fShort ? "aborted" : Info::tr("aborted"); + case MachineState_AbortedSaved: + return fShort ? "aborted-saved" : Info::tr("aborted-saved"); + case MachineState_Running: + return fShort ? "running" : Info::tr("running"); + case MachineState_Paused: + return fShort ? "paused" : Info::tr("paused"); + case MachineState_Stuck: + return fShort ? "gurumeditation" : Info::tr("guru meditation"); + case MachineState_Teleporting: + return fShort ? "teleporting" : Info::tr("teleporting"); + case MachineState_LiveSnapshotting: + return fShort ? "livesnapshotting" : Info::tr("live snapshotting"); + case MachineState_Starting: + return fShort ? "starting" : Info::tr("starting"); + case MachineState_Stopping: + return fShort ? "stopping" : Info::tr("stopping"); + case MachineState_Saving: + return fShort ? "saving" : Info::tr("saving"); + case MachineState_Restoring: + return fShort ? "restoring" : Info::tr("restoring"); + case MachineState_TeleportingPausedVM: + return fShort ? "teleportingpausedvm" : Info::tr("teleporting paused vm"); + case MachineState_TeleportingIn: + return fShort ? "teleportingin" : Info::tr("teleporting (incoming)"); + case MachineState_DeletingSnapshotOnline: + return fShort ? "deletingsnapshotlive" : Info::tr("deleting snapshot live"); + case MachineState_DeletingSnapshotPaused: + return fShort ? "deletingsnapshotlivepaused" : Info::tr("deleting snapshot live paused"); + case MachineState_OnlineSnapshotting: + return fShort ? "onlinesnapshotting" : Info::tr("online snapshotting"); + case MachineState_RestoringSnapshot: + return fShort ? "restoringsnapshot" : Info::tr("restoring snapshot"); + case MachineState_DeletingSnapshot: + return fShort ? "deletingsnapshot" : Info::tr("deleting snapshot"); + case MachineState_SettingUp: + return fShort ? "settingup" : Info::tr("setting up"); + case MachineState_Snapshotting: + return fShort ? "snapshotting" : Info::tr("offline snapshotting"); + default: + break; + } + return Info::tr("unknown"); +} + +const char *facilityStateToName(AdditionsFacilityStatus_T faStatus, bool fShort) +{ + switch (faStatus) + { + case AdditionsFacilityStatus_Inactive: + return fShort ? "inactive" : Info::tr("not active"); + case AdditionsFacilityStatus_Paused: + return fShort ? "paused" : Info::tr("paused"); + case AdditionsFacilityStatus_PreInit: + return fShort ? "preinit" : Info::tr("pre-initializing"); + case AdditionsFacilityStatus_Init: + return fShort ? "init" : Info::tr("initializing"); + case AdditionsFacilityStatus_Active: + return fShort ? "active" : Info::tr("active/running"); + case AdditionsFacilityStatus_Terminating: + return fShort ? "terminating" : Info::tr("terminating"); + case AdditionsFacilityStatus_Terminated: + return fShort ? "terminated" : Info::tr("terminated"); + case AdditionsFacilityStatus_Failed: + return fShort ? "failed" : Info::tr("failed"); + case AdditionsFacilityStatus_Unknown: + default: + break; + } + return Info::tr("unknown"); +} + +static const char *storageControllerTypeToName(StorageControllerType_T enmCtlType, bool fMachineReadable = false) +{ + switch (enmCtlType) + { + case StorageControllerType_LsiLogic: + return "LsiLogic"; + case StorageControllerType_LsiLogicSas: + return "LsiLogicSas"; + case StorageControllerType_BusLogic: + return "BusLogic"; + case StorageControllerType_IntelAhci: + return "IntelAhci"; + case StorageControllerType_PIIX3: + return "PIIX3"; + case StorageControllerType_PIIX4: + return "PIIX4"; + case StorageControllerType_ICH6: + return "ICH6"; + case StorageControllerType_I82078: + return "I82078"; + case StorageControllerType_USB: + return "USB"; + case StorageControllerType_NVMe: + return "NVMe"; + case StorageControllerType_VirtioSCSI: + return "VirtioSCSI"; + default: + return fMachineReadable ? "unknown" : Info::tr("unknown"); + } +} + + +DECLINLINE(bool) doesMachineReadableStringNeedEscaping(const char *psz) +{ + return psz == NULL + || *psz == '\0' + || strchr(psz, '"') != NULL + || strchr(psz, '\\') != NULL; +} + + +/** + * This simply outputs the string adding necessary escaping and nothing else. + */ +void outputMachineReadableStringWorker(const char *psz) +{ + for (;;) + { + const char *pszDoubleQuote = strchr(psz, '"'); + const char *pszSlash = strchr(psz, '\\'); + const char *pszNext; + if (pszSlash) + pszNext = !pszDoubleQuote || (uintptr_t)pszSlash < (uintptr_t)pszDoubleQuote ? pszSlash : pszDoubleQuote; + else if (pszDoubleQuote) + pszNext = pszDoubleQuote; + else + { + RTStrmWrite(g_pStdOut, psz, strlen(psz)); + break; + } + RTStrmWrite(g_pStdOut, psz, pszNext - psz); + char const szTmp[2] = { '\\', *pszNext }; + RTStrmWrite(g_pStdOut, szTmp, sizeof(szTmp)); + + psz = pszNext + 1; + } +} + + +/** + * This takes care of escaping double quotes and slashes that the string might + * contain. + * + * @param pszName The variable name. + * @param pszValue The value. + * @param fQuoteName Whether to unconditionally quote the name or not. + * @param fNewline Whether to automatically add a newline after the value. + */ +void outputMachineReadableString(const char *pszName, const char *pszValue, bool fQuoteName /*=false*/, bool fNewline /*=true*/) +{ + if (!fQuoteName) + fQuoteName = strchr(pszName, '=') != NULL; + bool const fEscapeName = doesMachineReadableStringNeedEscaping(pszName); + bool const fEscapeValue = doesMachineReadableStringNeedEscaping(pszValue); + if (!fEscapeName && !fEscapeValue) + { + if (fNewline) + RTPrintf(!fQuoteName ? "%s=\"%s\"\n" : "\"%s\"=\"%s\"\n", pszName, pszValue); + else + RTPrintf(!fQuoteName ? "%s=\"%s\"" : "\"%s\"=\"%s\"", pszName, pszValue); + } + else + { + /* The name and string quotation: */ + if (!fEscapeName) + RTPrintf(fQuoteName ? "\"%s\"=\"" : "%s=\"", pszName); + else + { + if (fQuoteName) + RTStrmWrite(g_pStdOut, RT_STR_TUPLE("\"")); + outputMachineReadableStringWorker(pszName); + if (fQuoteName) + RTStrmWrite(g_pStdOut, RT_STR_TUPLE("\"=\"")); + else + RTStrmWrite(g_pStdOut, RT_STR_TUPLE("=\"")); + } + + /* the value and the closing quotation */ + outputMachineReadableStringWorker(pszValue); + if (fNewline) + RTStrmWrite(g_pStdOut, RT_STR_TUPLE("\"\n")); + else + RTStrmWrite(g_pStdOut, RT_STR_TUPLE("\"")); + } +} + + +/** + * This takes care of escaping double quotes and slashes that the string might + * contain. + * + * @param pszName The variable name. + * @param pbstrValue The value. + * @param fQuoteName Whether to unconditionally quote the name or not. + * @param fNewline Whether to automatically add a newline after the value. + */ +void outputMachineReadableString(const char *pszName, Bstr const *pbstrValue, bool fQuoteName /*=false*/, bool fNewline /*=true*/) +{ + com::Utf8Str strValue(*pbstrValue); + outputMachineReadableString(pszName, strValue.c_str(), fQuoteName, fNewline); +} + + +/** + * Variant that allows formatting the name string, C string value. + * + * @param pszValue The value. + * @param fQuoteName Whether to unconditionally quote the name or not. + * @param pszNameFmt The variable name. + */ +void outputMachineReadableStringWithFmtName(const char *pszValue, bool fQuoteName, const char *pszNameFmt, ...) +{ + com::Utf8Str strName; + va_list va; + va_start(va, pszNameFmt); + strName.printfV(pszNameFmt, va); + va_end(va); + + outputMachineReadableString(strName.c_str(), pszValue, fQuoteName); +} + + +/** + * Variant that allows formatting the name string, Bstr value. + * + * @param pbstrValue The value. + * @param fQuoteName Whether to unconditionally quote the name or not. + * @param pszNameFmt The variable name. + */ +void outputMachineReadableStringWithFmtName(com::Bstr const *pbstrValue, bool fQuoteName, const char *pszNameFmt, ...) +{ + com::Utf8Str strName; + va_list va; + va_start(va, pszNameFmt); + strName.printfV(pszNameFmt, va); + va_end(va); + + outputMachineReadableString(strName.c_str(), pbstrValue, fQuoteName); +} + + +/** + * Machine readable outputting of a boolean value. + */ +void outputMachineReadableBool(const char *pszName, BOOL const *pfValue) +{ + RTPrintf("%s=\"%s\"\n", pszName, *pfValue ? "on" : "off"); +} + + +/** + * Machine readable outputting of a boolean value. + */ +void outputMachineReadableBool(const char *pszName, bool const *pfValue) +{ + RTPrintf("%s=\"%s\"\n", pszName, *pfValue ? "on" : "off"); +} + + +/** + * Machine readable outputting of a ULONG value. + */ +void outputMachineReadableULong(const char *pszName, ULONG *puValue) +{ + RTPrintf("%s=\"%u\"\n", pszName, *puValue); +} + + +/** + * Machine readable outputting of a LONG64 value. + */ +void outputMachineReadableLong64(const char *pszName, LONG64 *puValue) +{ + RTPrintf("%s=\"%llu\"\n", pszName, *puValue); +} + + +/** + * Helper for parsing extra data config. + * @returns true, false, or -1 if invalid. + */ +static int parseCfgmBool(Bstr const *pbstr) +{ + /* GetExtraData returns empty strings if the requested data wasn't + found, so fend that off first: */ + size_t cwcLeft = pbstr->length(); + if (!cwcLeft) + return false; + PCRTUTF16 pwch = pbstr->raw(); + + /* Skip type prefix: */ + if ( cwcLeft >= 8 + && pwch[0] == 'i' + && pwch[1] == 'n' + && pwch[2] == 't' + && pwch[3] == 'e' + && pwch[4] == 'g' + && pwch[5] == 'e' + && pwch[6] == 'r' + && pwch[7] == ':') + { + pwch += 8; + cwcLeft -= 8; + } + + /* Hex prefix? */ + bool fHex = false; + if ( cwcLeft >= 2 + && pwch[0] == '0' + && (pwch[1] == 'x' || pwch[1] == 'X')) + { + pwch += 2; + cwcLeft -= 2; + fHex = true; + } + + /* Empty string is wrong: */ + if (cwcLeft == 0) + return -1; + + /* Check that it's all digits and return when we find a non-zero + one or reaches the end: */ + do + { + RTUTF16 const wc = *pwch++; + if (!RT_C_IS_DIGIT(wc) && (!fHex || !RT_C_IS_XDIGIT(wc))) + return -1; + if (wc != '0') + return true; + } while (--cwcLeft > 0); + return false; +} + + +/** + * 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 Info::tr("Null"); + case BandwidthGroupType_Disk: return Info::tr("Disk"); + case BandwidthGroupType_Network: return Info::tr("Network"); +#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK + case BandwidthGroupType_32BitHack: break; /* Shut up compiler warnings. */ +#endif + } + return Info::tr("unknown"); +} + +HRESULT showBandwidthGroups(ComPtr &bwCtrl, + VMINFO_DETAILS details) +{ + SafeIfaceArray bwGroups; + CHECK_ERROR2I_RET(bwCtrl, GetAllBandwidthGroups(ComSafeArrayAsOutParam(bwGroups)), hrcCheck); + + if (details != VMINFO_MACHINEREADABLE) + RTPrintf(bwGroups.size() != 0 ? "\n" : Info::tr("\n")); + for (size_t i = 0; i < bwGroups.size(); i++) + { + Bstr strName; + CHECK_ERROR2I_RET(bwGroups[i], COMGETTER(Name)(strName.asOutParam()), hrcCheck); + BandwidthGroupType_T enmType; + CHECK_ERROR2I_RET(bwGroups[i], COMGETTER(Type)(&enmType), hrcCheck); + LONG64 cbMaxPerSec; + CHECK_ERROR2I_RET(bwGroups[i], COMGETTER(MaxBytesPerSec)(&cbMaxPerSec), hrcCheck); + + const char *pszType = bwGroupTypeToString(enmType); + if (details == VMINFO_MACHINEREADABLE) + { + /* Complicated condensed format. */ + char szName[64]; + RTStrPrintf(szName, sizeof(szName), "BandwidthGroup%zu", i); + outputMachineReadableString(szName, &strName, false /*fQuoteName*/, false /*fNewline*/); + RTPrintf(",%s,%RI64\n", pszType, cbMaxPerSec); + } + else + { + if (cbMaxPerSec == 0) + { + RTPrintf(Info::tr("#%zu: Name: '%ls', Type: %s, Limit: none (disabled)\n"), i, strName.raw(), pszType); + continue; + } + + /* translate to human readable units.*/ + const char *pszUnit; + LONG64 cUnits; + if (!(cbMaxPerSec % _1G)) + { + cUnits = cbMaxPerSec / _1G; + pszUnit = "GiB/s"; + } + else if (!(cbMaxPerSec % _1M)) + { + cUnits = cbMaxPerSec / _1M; + pszUnit = "MiB/s"; + } + else if (!(cbMaxPerSec % _1K)) + { + cUnits = cbMaxPerSec / _1K; + pszUnit = "KiB/s"; + } + else + { + cUnits = cbMaxPerSec; + pszUnit = "bytes/s"; + } + + /* + * 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. + */ + if ( enmType == BandwidthGroupType_Network + && !(cbMaxPerSec % 125) ) + { + LONG64 cNetUnits = cbMaxPerSec / 125; + const char *pszNetUnit = "kbps"; + if (!(cNetUnits % 1000000)) + { + cNetUnits /= 1000000; + pszNetUnit = "Gbps"; + } + else if (!(cNetUnits % 1000)) + { + cNetUnits /= 1000; + pszNetUnit = "Mbps"; + } + RTPrintf(Info::tr("#%zu: Name: '%ls', Type: %s, Limit: %RI64 %s (%RI64 %s)\n"), + i, strName.raw(), pszType, cNetUnits, pszNetUnit, cUnits, pszUnit); + } + else + RTPrintf(Info::tr("#%zu: Name: '%ls', Type: %s, Limit: %RI64 %s\n"), i, strName.raw(), pszType, cUnits, pszUnit); + } + } + + return VINF_SUCCESS; +} + +/** Shows a shared folder. */ +static HRESULT showSharedFolder(ComPtr &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(Info::tr("Name: '%ls', Host path: '%ls' (%s), %s%s"), + name.raw(), hostPath.raw(), pszDesc, writable ? Info::tr("writable") : Info::tr("readonly"), + fAutoMount ? Info::tr(", auto-mount") : ""); + if (bstrAutoMountPoint.isNotEmpty()) + RTPrintf(Info::tr(", mount-point: '%ls'\n"), bstrAutoMountPoint.raw()); + else + RTPrintf("\n"); + } + return S_OK; +} + +/** Displays a list of IUSBDevices or IHostUSBDevices. */ +template +static HRESULT showUsbDevices(SafeIfaceArray &coll, const char *pszPfx, + const char *pszName, VMINFO_DETAILS details) +{ + if (coll.size() > 0) + { + if (details != VMINFO_MACHINEREADABLE) + RTPrintf("%-28s\n\n", pszName); + for (size_t i = 0; i < coll.size(); ++i) + { + ComPtr dev = coll[i]; + char szValue[128]; + char szNm[80]; + + 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), Info::tr("VendorId:"), "", "%#06x", "%#06x (%04X)"); + SHOW_USHORT_PROP_EX2(dev, ProductId, FmtNm(szNm, "%sProductId%zu", pszPfx, i + 1), Info::tr("ProductId:"), "", "%#06x", "%#06x (%04X)"); + + USHORT bcdRevision; + CHECK_ERROR2I_RET(dev, COMGETTER(Revision)(&bcdRevision), hrcCheck); + 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), Info::tr("Revision:"), szValue); + + SHOW_STRING_PROP_NOT_EMPTY(dev, Manufacturer, FmtNm(szNm, "%sManufacturer%zu", pszPfx, i + 1), Info::tr("Manufacturer:")); + SHOW_STRING_PROP_NOT_EMPTY(dev, Product, FmtNm(szNm, "%sProduct%zu", pszPfx, i + 1), Info::tr("Product:")); + SHOW_STRING_PROP_NOT_EMPTY(dev, SerialNumber, FmtNm(szNm, "%sSerialNumber%zu", pszPfx, i + 1), Info::tr("SerialNumber:")); + SHOW_STRING_PROP_NOT_EMPTY(dev, Address, FmtNm(szNm, "%sAddress%zu", pszPfx, i + 1), Info::tr("Address:")); + + if (details != VMINFO_MACHINEREADABLE) + RTPrintf("\n"); + } + } + else if (details != VMINFO_MACHINEREADABLE) + RTPrintf("%-28s %s\n", pszName, Info::tr("")); + return S_OK; +} + +/** Displays the medium attachments of the given controller. */ +static HRESULT showMediumAttachments(ComPtr &machine, ComPtr ptrStorageCtl, VMINFO_DETAILS details) +{ + Bstr bstrStorageCtlName; + CHECK_ERROR2I_RET(ptrStorageCtl, COMGETTER(Name)(bstrStorageCtlName.asOutParam()), hrcCheck); + ULONG cDevices; + CHECK_ERROR2I_RET(ptrStorageCtl, COMGETTER(MaxDevicesPerPortCount)(&cDevices), hrcCheck); + ULONG cPorts; + CHECK_ERROR2I_RET(ptrStorageCtl, COMGETTER(PortCount)(&cPorts), hrcCheck); + + for (ULONG i = 0; i < cPorts; ++ i) + { + for (ULONG k = 0; k < cDevices; ++ k) + { + ComPtr mediumAttach; + HRESULT hrc = machine->GetMediumAttachment(bstrStorageCtlName.raw(), i, k, mediumAttach.asOutParam()); + if (!SUCCEEDED(hrc) && hrc != VBOX_E_OBJECT_NOT_FOUND) + { + com::GlueHandleComError(machine, "GetMediumAttachment", hrc, __FILE__, __LINE__); + return hrc; + } + + BOOL fIsEjected = FALSE; + BOOL fTempEject = FALSE; + BOOL fHotPlug = FALSE; + BOOL fNonRotational = FALSE; + BOOL fDiscard = FALSE; + DeviceType_T devType = DeviceType_Null; + if (mediumAttach) + { + CHECK_ERROR2I_RET(mediumAttach, COMGETTER(TemporaryEject)(&fTempEject), hrcCheck); + CHECK_ERROR2I_RET(mediumAttach, COMGETTER(IsEjected)(&fIsEjected), hrcCheck); + CHECK_ERROR2I_RET(mediumAttach, COMGETTER(Type)(&devType), hrcCheck); + CHECK_ERROR2I_RET(mediumAttach, COMGETTER(HotPluggable)(&fHotPlug), hrcCheck); + CHECK_ERROR2I_RET(mediumAttach, COMGETTER(NonRotational)(&fNonRotational), hrcCheck); + CHECK_ERROR2I_RET(mediumAttach, COMGETTER(Discard)(&fDiscard), hrcCheck); + } + + ComPtr medium; + hrc = machine->GetMedium(bstrStorageCtlName.raw(), i, k, medium.asOutParam()); + if (SUCCEEDED(hrc) && medium) + { + BOOL fPassthrough = FALSE; + if (mediumAttach) + { + CHECK_ERROR2I_RET(mediumAttach, COMGETTER(Passthrough)(&fPassthrough), hrcCheck); + } + + Bstr bstrFilePath; + CHECK_ERROR2I_RET(medium, COMGETTER(Location)(bstrFilePath.asOutParam()), hrcCheck); + Bstr bstrUuid; + CHECK_ERROR2I_RET(medium, COMGETTER(Id)(bstrUuid.asOutParam()), hrcCheck); + + if (details != VMINFO_MACHINEREADABLE) + RTPrintf(Info::tr(" Port %u, Unit %u: UUID: %ls%s%s%s%s%s%s\n Location: \"%ls\"\n"), + i, k, bstrUuid.raw(), + fPassthrough ? Info::tr(", passthrough enabled") : "", + fTempEject ? Info::tr(", temp eject") : "", + fIsEjected ? Info::tr(", ejected") : "", + fHotPlug ? Info::tr(", hot-pluggable") : "", + fNonRotational ? Info::tr(", non-rotational (SSD)") : "", + fDiscard ? Info::tr(", discards unused blocks") : "", + bstrFilePath.raw()); + else + { + /* Note! dvdpassthough, tempeject and IsEjected was all missed the port + and unit bits prior to VBox 7.0. */ + /** @todo This would look better on the "%ls-%d-%d-{tag}" form! */ + outputMachineReadableStringWithFmtName(&bstrFilePath, + true, "%ls-%d-%d", bstrStorageCtlName.raw(), i, k); + outputMachineReadableStringWithFmtName(&bstrUuid, + true, "%ls-ImageUUID-%d-%d", bstrStorageCtlName.raw(), i, k); + + if (fPassthrough) + outputMachineReadableStringWithFmtName("on", + true, "%ls-dvdpassthrough-%d-%d", bstrStorageCtlName.raw(), i, k); + if (devType == DeviceType_DVD) + { + outputMachineReadableStringWithFmtName(fTempEject ? "on" : "off", + true, "%ls-tempeject-%d-%d", bstrStorageCtlName.raw(), i, k); + outputMachineReadableStringWithFmtName(fIsEjected ? "on" : "off", + true, "%ls-IsEjected-%d-%d", bstrStorageCtlName.raw(), i, k); + } + + if ( bstrStorageCtlName.compare(Bstr("SATA"), Bstr::CaseInsensitive)== 0 + || bstrStorageCtlName.compare(Bstr("USB"), Bstr::CaseInsensitive)== 0) + outputMachineReadableStringWithFmtName(fHotPlug ? "on" : "off", + true, "%ls-hot-pluggable-%d-%d", bstrStorageCtlName.raw(), + i, k); + + outputMachineReadableStringWithFmtName(fNonRotational ? "on" : "off", + true, "%ls-nonrotational-%d-%d", bstrStorageCtlName.raw(), i, k); + outputMachineReadableStringWithFmtName(fDiscard ? "on" : "off", + true, "%ls-discard-%d-%d", bstrStorageCtlName.raw(), i, k); + } + } + else if (SUCCEEDED(hrc)) + { + if (details != VMINFO_MACHINEREADABLE) + RTPrintf(Info::tr(" Port %u, Unit %u: Empty%s%s\n"), i, k, + fTempEject ? Info::tr(", temp eject") : "", + fIsEjected ? Info::tr(", ejected") : ""); + else + { + outputMachineReadableStringWithFmtName("emptydrive", true, "%ls-%d-%d", bstrStorageCtlName.raw(), i, k); + if (devType == DeviceType_DVD) + outputMachineReadableStringWithFmtName(fIsEjected ? "on" : "off", + true, "%ls-IsEjected-%d-%d", bstrStorageCtlName.raw(), i, k); + } + } + else if (details == VMINFO_MACHINEREADABLE) + outputMachineReadableStringWithFmtName("none", true, "%ls-%d-%d", bstrStorageCtlName.raw(), i, k); + else if (hrc != VBOX_E_OBJECT_NOT_FOUND) + RTPrintf(Info::tr(" Port %u, Unit %u: GetMedium failed: %Rhrc\n"), i, k, hrc); + + } + } + return S_OK; +} + + +#ifdef VBOX_WITH_IOMMU_AMD +static const char *iommuTypeToString(IommuType_T iommuType, VMINFO_DETAILS details) +{ + switch (iommuType) + { + case IommuType_None: + if (details == VMINFO_MACHINEREADABLE) + return "none"; + return Info::tr("None"); + + case IommuType_Automatic: + if (details == VMINFO_MACHINEREADABLE) + return "automatic"; + return Info::tr("Automatic"); + + case IommuType_AMD: + if (details == VMINFO_MACHINEREADABLE) + return "amd"; + return "AMD"; + + case IommuType_Intel: + if (details == VMINFO_MACHINEREADABLE) + return "intel"; + return "Intel"; + + default: + if (details == VMINFO_MACHINEREADABLE) + return "unknown"; + return Info::tr("Unknown"); + } +} +#endif + +static const char *paravirtProviderToString(ParavirtProvider_T provider, VMINFO_DETAILS details) +{ + switch (provider) + { + case ParavirtProvider_None: + if (details == VMINFO_MACHINEREADABLE) + return "none"; + return Info::tr("None"); + + case ParavirtProvider_Default: + if (details == VMINFO_MACHINEREADABLE) + return "default"; + return Info::tr("Default"); + + case ParavirtProvider_Legacy: + if (details == VMINFO_MACHINEREADABLE) + return "legacy"; + return Info::tr("Legacy"); + + case ParavirtProvider_Minimal: + if (details == VMINFO_MACHINEREADABLE) + return "minimal"; + return Info::tr("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 Info::tr("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 pVirtualBox, + ComPtr machine, + ComPtr pSession, + VMINFO_DETAILS details /*= VMINFO_NONE*/) +{ + HRESULT hrc; + ComPtr pConsole; + if (pSession) + pSession->COMGETTER(Console)(pConsole.asOutParam()); + + char szNm[80]; + char szValue[256]; + + /* + * 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(Info::tr("\"\" {%s}\n"), Utf8Str(uuid).c_str()); + else + { + if (details == VMINFO_MACHINEREADABLE) + RTPrintf("name=\"\"\n"); + else + RTPrintf(Info::tr("Name: \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; + hrc = machine->COMGETTER(SettingsFilePath)(settingsFilePath.asOutParam()); + RTPrintf(Info::tr("Config file: %ls\n"), settingsFilePath.raw()); + + Bstr strCipher; + Bstr strPasswordId; + HRESULT hrc2 = machine->GetEncryptionSettings(strCipher.asOutParam(), strPasswordId.asOutParam()); + if (SUCCEEDED(hrc2)) + { + RTPrintf("Encryption: enabled\n"); + RTPrintf("Cipher: %ls\n", strCipher.raw()); + RTPrintf("Password ID: %ls\n", strPasswordId.raw()); + } + else + RTPrintf("Encryption: disabled\n"); + + ComPtr accessError; + hrc = machine->COMGETTER(AccessError)(accessError.asOutParam()); + RTPrintf(Info::tr("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", Info::tr("Name:")); + { + Bstr strCipher; + Bstr strPasswordId; + HRESULT hrc2 = machine->GetEncryptionSettings(strCipher.asOutParam(), strPasswordId.asOutParam()); + if (SUCCEEDED(hrc2)) + { + RTPrintf("Encryption: enabled\n"); + RTPrintf("Cipher: %ls\n", strCipher.raw()); + RTPrintf("Password ID: %ls\n", strPasswordId.raw()); + } + else + RTPrintf("Encryption: disabled\n"); + } + SHOW_STRINGARRAY_PROP( machine, Groups, "groups", Info::tr("Groups:")); + Bstr osTypeId; + CHECK_ERROR2I_RET(machine, COMGETTER(OSTypeId)(osTypeId.asOutParam()), hrcCheck); + ComPtr osType; + pVirtualBox->GetGuestOSType(osTypeId.raw(), osType.asOutParam()); + if (!osType.isNull()) + SHOW_STRING_PROP( osType, Description, "ostype", Info::tr("Guest OS:")); + else + SHOW_STRING_PROP( machine, OSTypeId, "ostype", Info::tr("Guest OS:")); + SHOW_UUID_PROP( machine, Id, "UUID", "UUID:"); + SHOW_STRING_PROP( machine, SettingsFilePath, "CfgFile", Info::tr("Config file:")); + SHOW_STRING_PROP( machine, SnapshotFolder, "SnapFldr", Info::tr("Snapshot folder:")); + SHOW_STRING_PROP( machine, LogFolder, "LogFldr", Info::tr("Log folder:")); + SHOW_UUID_PROP( machine, HardwareUUID, "hardwareuuid", Info::tr("Hardware UUID:")); + SHOW_ULONG_PROP( machine, MemorySize, "memory", Info::tr("Memory size:"), "MB"); + SHOW_BOOLEAN_PROP( machine, PageFusionEnabled, "pagefusion", Info::tr("Page Fusion:")); + ComPtr pGraphicsAdapter; + machine->COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam()); + SHOW_ULONG_PROP(pGraphicsAdapter, VRAMSize, "vram", Info::tr("VRAM size:"), "MB"); + SHOW_ULONG_PROP( machine, CPUExecutionCap, "cpuexecutioncap", Info::tr("CPU exec cap:"), "%"); + SHOW_BOOLEAN_PROP( machine, HPETEnabled, "hpet", Info::tr("HPET:")); + SHOW_STRING_PROP_MAJ( machine, CPUProfile, "cpu-profile", Info::tr("CPUProfile:"), "host", 6); + + ChipsetType_T chipsetType; + CHECK_ERROR2I_RET(machine, COMGETTER(ChipsetType)(&chipsetType), hrcCheck); + const char *pszChipsetType; + switch (chipsetType) + { + case ChipsetType_Null: + if (details == VMINFO_MACHINEREADABLE) + pszChipsetType = "invalid"; + else + pszChipsetType = Info::tr("invalid"); + break; + case ChipsetType_PIIX3: pszChipsetType = "piix3"; break; + case ChipsetType_ICH9: pszChipsetType = "ich9"; break; + default: + AssertFailed(); + if (details == VMINFO_MACHINEREADABLE) + pszChipsetType = "unknown"; + else + pszChipsetType = Info::tr("unknown"); + break; + } + SHOW_UTF8_STRING("chipset", Info::tr("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(); + if (details == VMINFO_MACHINEREADABLE) + pszFirmwareType = "unknown"; + else + pszFirmwareType = Info::tr("unknown"); + break; + } + SHOW_UTF8_STRING("firmware", Info::tr("Firmware:"), pszFirmwareType); + + SHOW_ULONG_PROP( machine, CPUCount, "cpus", Info::tr("Number of CPUs:"), ""); + SHOW_BOOLEAN_METHOD( machine, GetCPUProperty(CPUPropertyType_PAE, &f), "pae", "PAE:"); + SHOW_BOOLEAN_METHOD( machine, GetCPUProperty(CPUPropertyType_LongMode, &f), "longmode", Info::tr("Long Mode:")); + SHOW_BOOLEAN_METHOD( machine, GetCPUProperty(CPUPropertyType_TripleFaultReset, &f), "triplefaultreset", Info::tr("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", Info::tr("Nested VT-x/AMD-V:")); + SHOW_ULONG_PROP( machine, CPUIDPortabilityLevel, "cpuid-portability-level", Info::tr("CPUID Portability Level:"), ""); + + if (details != VMINFO_MACHINEREADABLE) + RTPrintf("%-28s ", Info::tr("CPUID overrides:")); + ULONG uOrdinal = 0; + for (uOrdinal = 0; uOrdinal < _4K; uOrdinal++) + { + ULONG uLeaf, uSubLeaf, uEAX, uEBX, uECX, uEDX; + hrc = machine->GetCPUIDLeafByOrdinal(uOrdinal, &uLeaf, &uSubLeaf, &uEAX, &uEBX, &uECX, &uEDX); + if (SUCCEEDED(hrc)) + { + if (details == VMINFO_MACHINEREADABLE) + RTPrintf("cpuid=%08x,%08x,%08x,%08x,%08x,%08x", uLeaf, uSubLeaf, uEAX, uEBX, uECX, uEDX); + else + { + if (!uOrdinal) + RTPrintf(Info::tr("Leaf no. EAX EBX ECX EDX\n")); + RTPrintf("%-28s %08x/%03x %08x %08x %08x %08x\n", "", uLeaf, uSubLeaf, uEAX, uEBX, uECX, uEDX); + } + } + else + { + if (hrc != E_INVALIDARG) + com::GlueHandleComError(machine, "GetCPUIDLeaf", hrc, __FILE__, __LINE__); + break; + } + } + if (!uOrdinal && details != VMINFO_MACHINEREADABLE) + RTPrintf(Info::tr("None\n")); + + ComPtr biosSettings; + CHECK_ERROR2I_RET(machine, COMGETTER(BIOSSettings)(biosSettings.asOutParam()), hrcCheck); + + ComPtr nvramStore; + CHECK_ERROR2I_RET(machine, COMGETTER(NonVolatileStore)(nvramStore.asOutParam()), hrcCheck); + + BIOSBootMenuMode_T bootMenuMode; + CHECK_ERROR2I_RET(biosSettings, COMGETTER(BootMenuMode)(&bootMenuMode), hrcCheck); + const char *pszBootMenu; + switch (bootMenuMode) + { + case BIOSBootMenuMode_Disabled: + if (details == VMINFO_MACHINEREADABLE) + pszBootMenu = "disabled"; + else + pszBootMenu = Info::tr("disabled"); + break; + case BIOSBootMenuMode_MenuOnly: + if (details == VMINFO_MACHINEREADABLE) + pszBootMenu = "menuonly"; + else + pszBootMenu = Info::tr("menu only"); + break; + default: + if (details == VMINFO_MACHINEREADABLE) + pszBootMenu = "messageandmenu"; + else + pszBootMenu = Info::tr("message and menu"); + } + SHOW_UTF8_STRING("bootmenu", Info::tr("Boot menu mode:"), pszBootMenu); + + ComPtr 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" : Info::tr("Floppy"); + else if (bootOrder == DeviceType_DVD) + pszDevice = details == VMINFO_MACHINEREADABLE ? "dvd" : "DVD"; + else if (bootOrder == DeviceType_HardDisk) + pszDevice = details == VMINFO_MACHINEREADABLE ? "disk" : Info::tr("HardDisk"); + else if (bootOrder == DeviceType_Network) + pszDevice = details == VMINFO_MACHINEREADABLE ? "net" : Info::tr("Network"); + else if (bootOrder == DeviceType_USB) + pszDevice = details == VMINFO_MACHINEREADABLE ? "usb" : "USB"; + else if (bootOrder == DeviceType_SharedFolder) + pszDevice = details == VMINFO_MACHINEREADABLE ? "sharedfolder" : Info::tr("Shared Folder"); + else + pszDevice = details == VMINFO_MACHINEREADABLE ? "none" : Info::tr("Not Assigned"); + SHOW_UTF8_STRING(FmtNm(szNm, "boot%u", i), FmtNm(szNm, Info::tr("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: + if (details == VMINFO_MACHINEREADABLE) + pszAPIC = "disabled"; + else + pszAPIC = Info::tr("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", Info::tr("BIOS APIC mode:"), pszAPIC); + + SHOW_LONG64_PROP(biosSettings, TimeOffset, "biossystemtimeoffset", Info::tr("Time offset:"), Info::tr("ms")); + Bstr bstrNVRAMFile; + CHECK_ERROR2I_RET(nvramStore, COMGETTER(NonVolatileStorageFile)(bstrNVRAMFile.asOutParam()), hrcCheck); + if (bstrNVRAMFile.isNotEmpty()) + SHOW_BSTR_STRING("BIOS NVRAM File", Info::tr("BIOS NVRAM File:"), bstrNVRAMFile); + SHOW_BOOLEAN_PROP_EX(machine, RTCUseUTC, "rtcuseutc", Info::tr("RTC:"), "UTC", Info::tr("local time")); + SHOW_BOOLEAN_METHOD(machine, GetHWVirtExProperty(HWVirtExPropertyType_Enabled, &f), "hwvirtex", Info::tr("Hardware Virtualization:")); + SHOW_BOOLEAN_METHOD(machine, GetHWVirtExProperty(HWVirtExPropertyType_NestedPaging, &f),"nestedpaging", Info::tr("Nested Paging:")); + SHOW_BOOLEAN_METHOD(machine, GetHWVirtExProperty(HWVirtExPropertyType_LargePages, &f), "largepages", Info::tr("Large Pages:")); + SHOW_BOOLEAN_METHOD(machine, GetHWVirtExProperty(HWVirtExPropertyType_VPID, &f), "vtxvpid", "VT-x VPID:"); + SHOW_BOOLEAN_METHOD(machine, GetHWVirtExProperty(HWVirtExPropertyType_UnrestrictedExecution, &f), "vtxux", Info::tr("VT-x Unrestricted Exec.:")); + SHOW_BOOLEAN_METHOD(machine, GetHWVirtExProperty(HWVirtExPropertyType_VirtVmsaveVmload, &f), "virtvmsavevmload", Info::tr("AMD-V Virt. Vmsave/Vmload:")); + +#ifdef VBOX_WITH_IOMMU_AMD + IommuType_T iommuType; + CHECK_ERROR2I_RET(machine, COMGETTER(IommuType)(&iommuType), hrcCheck); + const char *pszIommuType = iommuTypeToString(iommuType, details); + SHOW_UTF8_STRING("iommu", "IOMMU:", pszIommuType); +#endif + + ParavirtProvider_T paravirtProvider; + CHECK_ERROR2I_RET(machine, COMGETTER(ParavirtProvider)(¶virtProvider), hrcCheck); + const char *pszParavirtProvider = paravirtProviderToString(paravirtProvider, details); + SHOW_UTF8_STRING("paravirtprovider", Info::tr("Paravirt. Provider:"), pszParavirtProvider); + + ParavirtProvider_T effParavirtProvider; + CHECK_ERROR2I_RET(machine, GetEffectiveParavirtProvider(&effParavirtProvider), hrcCheck); + const char *pszEffParavirtProvider = paravirtProviderToString(effParavirtProvider, details); + SHOW_UTF8_STRING("effparavirtprovider", Info::tr("Effective Paravirt. Prov.:"), pszEffParavirtProvider); + + Bstr paravirtDebug; + CHECK_ERROR2I_RET(machine, COMGETTER(ParavirtDebug)(paravirtDebug.asOutParam()), hrcCheck); + if (paravirtDebug.isNotEmpty()) + SHOW_BSTR_STRING("paravirtdebug", Info::tr("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(Info::tr("%-28s %s (since %s)\n"), Info::tr("State:"), pszState, pszTime); + + GraphicsControllerType_T enmGraphics; + hrc = pGraphicsAdapter->COMGETTER(GraphicsControllerType)(&enmGraphics); + if (SUCCEEDED(hrc)) + { + const char *pszCtrl; + switch (enmGraphics) + { + case GraphicsControllerType_Null: + if (details == VMINFO_MACHINEREADABLE) + pszCtrl = "null"; + else + pszCtrl = Info::tr("Null"); + break; + case GraphicsControllerType_VBoxVGA: + if (details == VMINFO_MACHINEREADABLE) + pszCtrl = "vboxvga"; + else + pszCtrl = "VBoxVGA"; + break; + case GraphicsControllerType_VMSVGA: + if (details == VMINFO_MACHINEREADABLE) + pszCtrl = "vmsvga"; + else + pszCtrl = "VMSVGA"; + break; + case GraphicsControllerType_VBoxSVGA: + if (details == VMINFO_MACHINEREADABLE) + pszCtrl = "vboxsvga"; + else + pszCtrl = "VBoxSVGA"; + break; + default: + if (details == VMINFO_MACHINEREADABLE) + pszCtrl = "unknown"; + else + pszCtrl = Info::tr("Unknown"); + break; + } + + if (details == VMINFO_MACHINEREADABLE) + RTPrintf("graphicscontroller=\"%s\"\n", pszCtrl); + else + RTPrintf("%-28s %s\n", Info::tr("Graphics Controller:"), pszCtrl); + } + + SHOW_ULONG_PROP(pGraphicsAdapter, MonitorCount, "monitorcount", Info::tr("Monitor count:"), ""); + SHOW_BOOLEAN_PROP(pGraphicsAdapter, Accelerate3DEnabled, "accelerate3d", Info::tr("3D Acceleration:")); +#ifdef VBOX_WITH_VIDEOHWACCEL + SHOW_BOOLEAN_PROP(pGraphicsAdapter, Accelerate2DVideoEnabled, "accelerate2dvideo", Info::tr("2D Video Acceleration:")); +#endif + SHOW_BOOLEAN_PROP( machine, TeleporterEnabled, "teleporterenabled", Info::tr("Teleporter Enabled:")); + SHOW_ULONG_PROP( machine, TeleporterPort, "teleporterport", Info::tr("Teleporter Port:"), ""); + SHOW_STRING_PROP( machine, TeleporterAddress, "teleporteraddress", Info::tr("Teleporter Address:")); + SHOW_STRING_PROP( machine, TeleporterPassword, "teleporterpassword", Info::tr("Teleporter Password:")); + SHOW_BOOLEAN_PROP( machine, TracingEnabled, "tracing-enabled", Info::tr("Tracing Enabled:")); + SHOW_BOOLEAN_PROP( machine, AllowTracingToAccessVM, "tracing-allow-vm-access", Info::tr("Allow Tracing to Access VM:")); + SHOW_STRING_PROP( machine, TracingConfig, "tracing-config", Info::tr("Tracing Configuration:")); + SHOW_BOOLEAN_PROP( machine, AutostartEnabled, "autostart-enabled", Info::tr("Autostart Enabled:")); + SHOW_ULONG_PROP( machine, AutostartDelay, "autostart-delay", Info::tr("Autostart Delay:"), ""); + SHOW_STRING_PROP( machine, DefaultFrontend, "defaultfrontend", Info::tr("Default Frontend:")); + + VMProcPriority_T enmVMProcPriority; + CHECK_ERROR2I_RET(machine, COMGETTER(VMProcessPriority)(&enmVMProcPriority), hrcCheck); + const char *pszVMProcPriority; + switch (enmVMProcPriority) + { + case VMProcPriority_Flat: + if (details == VMINFO_MACHINEREADABLE) + pszVMProcPriority = "flat"; + else + pszVMProcPriority = Info::tr("flat"); + break; + case VMProcPriority_Low: + if (details == VMINFO_MACHINEREADABLE) + pszVMProcPriority = "low"; + else + pszVMProcPriority = Info::tr("low"); + break; + case VMProcPriority_Normal: + if (details == VMINFO_MACHINEREADABLE) + pszVMProcPriority = "normal"; + else + pszVMProcPriority = Info::tr("normal"); + break; + case VMProcPriority_High: + if (details == VMINFO_MACHINEREADABLE) + pszVMProcPriority = "high"; + else + pszVMProcPriority = Info::tr("high"); + break; + default: + if (details == VMINFO_MACHINEREADABLE) + pszVMProcPriority = "default"; + else + pszVMProcPriority = Info::tr("default"); + break; + } + SHOW_UTF8_STRING("vmprocpriority", Info::tr("VM process priority:"), pszVMProcPriority); + +/** @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 storageCtls; + CHECK_ERROR(machine, COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(storageCtls))); + if (storageCtls.size() > 0) + { + if (details != VMINFO_MACHINEREADABLE) + RTPrintf("%s\n", Info::tr("Storage Controllers:")); + + for (size_t i = 0; i < storageCtls.size(); ++i) + { + ComPtr storageCtl = storageCtls[i]; + + Bstr bstrName; + CHECK_ERROR2I_RET(storageCtl, COMGETTER(Name)(bstrName.asOutParam()), hrcCheck); + StorageControllerType_T enmCtlType = StorageControllerType_Null; + CHECK_ERROR2I_RET(storageCtl, COMGETTER(ControllerType)(&enmCtlType), hrcCheck); + ULONG uInstance = 0; + CHECK_ERROR2I_RET(storageCtl, COMGETTER(Instance)(&uInstance), hrcCheck); + ULONG cMaxPorts = 0; + CHECK_ERROR2I_RET(storageCtl, COMGETTER(MaxPortCount)(&cMaxPorts), hrcCheck); + ULONG cPorts = 0; + CHECK_ERROR2I_RET(storageCtl, COMGETTER(PortCount)(&cPorts), hrcCheck); + BOOL fBootable = FALSE; + CHECK_ERROR2I_RET(storageCtl, COMGETTER(Bootable)(&fBootable), hrcCheck); + if (details == VMINFO_MACHINEREADABLE) + { + outputMachineReadableString(FmtNm(szNm, "storagecontrollername%u", i), &bstrName); + outputMachineReadableString(FmtNm(szNm, "storagecontrollertype%u", i), + storageControllerTypeToName(enmCtlType, true)); + RTPrintf("storagecontrollerinstance%u=\"%u\"\n", i, uInstance); + RTPrintf("storagecontrollermaxportcount%u=\"%u\"\n", i, cMaxPorts); + RTPrintf("storagecontrollerportcount%u=\"%u\"\n", i, cPorts); + RTPrintf("storagecontrollerbootable%u=\"%s\"\n", i, fBootable ? "on" : "off"); + } + else + { + RTPrintf(Info::tr("#%u: '%ls', Type: %s, Instance: %u, Ports: %u (max %u), %s\n"), i, bstrName.raw(), + storageControllerTypeToName(enmCtlType, false), uInstance, cPorts, cMaxPorts, + fBootable ? Info::tr("Bootable") : Info::tr("Not bootable")); + hrc = showMediumAttachments(machine, storageCtl, details); + if (FAILED(hrc)) + return hrc; + } + } + } + else if (details != VMINFO_MACHINEREADABLE) + RTPrintf("%-28s %s\n", Info::tr("Storage Controllers:"), Info::tr("")); + + if (details == VMINFO_MACHINEREADABLE) + for (size_t j = 0; j < storageCtls.size(); ++ j) + { + hrc = showMediumAttachments(machine, storageCtls[j], details); + if (FAILED(hrc)) + return hrc; + } + + /* get the maximum amount of NICS */ + ULONG maxNICs = getMaxNics(pVirtualBox, machine); + + for (ULONG currentNIC = 0; currentNIC < maxNICs; currentNIC++) + { + ComPtr nic; + hrc = machine->GetNetworkAdapter(currentNIC, nic.asOutParam()); + if (SUCCEEDED(hrc) && nic) + { + FmtNm(szNm, details == VMINFO_MACHINEREADABLE ? "nic%u" : Info::tr("NIC %u:"), currentNIC + 1); + + BOOL fEnabled; + nic->COMGETTER(Enabled)(&fEnabled); + if (!fEnabled) + { + if (details == VMINFO_MACHINEREADABLE) + RTPrintf("%s=\"none\"\n", szNm); + else + RTPrintf(Info::tr("%-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 = Info::tr("none"); + break; + + case NetworkAttachmentType_NAT: + { + Bstr strNetwork; + ComPtr engine; + nic->COMGETTER(NATEngine)(engine.asOutParam()); + engine->COMGETTER(Network)(strNetwork.asOutParam()); + com::SafeArray 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) + /** @todo r=bird: This probably isn't good enough wrt escaping. */ + strNatForwardings.appendPrintf("Forwarding(%d)=\"%s,%s,%s,%s,%s,%s\"\n", + i, strName.c_str(), strProto.c_str(), + strHostIP.c_str(), strHostPort.c_str(), + strGuestIP.c_str(), strGuestPort.c_str()); + else + strNatForwardings.appendPrintf(Info::tr("NIC %d Rule(%d): name = %s, protocol = %s, host ip = %s, host port = %s, guest ip = %s, guest port = %s\n"), + 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.printf("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.printf(Info::tr("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.printf(Info::tr("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.printf(Info::tr("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.printf(Info::tr("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.printf(Info::tr("Generic '%ls'"), strGenericDriver.raw()); + + // show the generic properties + com::SafeArray aProperties; + com::SafeArray aValues; + hrc = nic->GetProperties(NULL, + ComSafeArrayAsOutParam(aProperties), + ComSafeArrayAsOutParam(aValues)); + if (SUCCEEDED(hrc)) + { + strAttachment += " { "; + for (unsigned i = 0; i < aProperties.size(); ++i) + strAttachment.appendPrintf(!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.printf(Info::tr("NAT Network '%s'"), Utf8Str(strNetwork).c_str()); + break; + } + +#ifdef VBOX_WITH_VMNET + case NetworkAttachmentType_HostOnlyNetwork: + { + Bstr strNetwork; + nic->COMGETTER(HostOnlyNetwork)(strNetwork.asOutParam()); + if (details == VMINFO_MACHINEREADABLE) + { + RTPrintf("hostonly-network%d=\"%ls\"\n", currentNIC + 1, strNetwork.raw()); + strAttachment = "hostonlynetwork"; + } + else + strAttachment.printf(Info::tr("Host Only Network '%s'"), Utf8Str(strNetwork).c_str()); + break; + } +#endif /* VBOX_WITH_VMNET */ + +#ifdef VBOX_WITH_CLOUD_NET + case NetworkAttachmentType_Cloud: + { + Bstr strNetwork; + nic->COMGETTER(CloudNetwork)(strNetwork.asOutParam()); + if (details == VMINFO_MACHINEREADABLE) + { + RTPrintf("cloud-network%d=\"%ls\"\n", currentNIC + 1, strNetwork.raw()); + strAttachment = "cloudnetwork"; + } + else + strAttachment.printf(Info::tr("Cloud Network '%s'"), Utf8Str(strNetwork).c_str()); + break; + } +#endif /* VBOX_WITH_CLOUD_NET */ + + default: + if (details == VMINFO_MACHINEREADABLE) + strAttachment = "unknown"; + else + strAttachment = Info::tr("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 = Info::tr("deny"); break; + case NetworkAdapterPromiscModePolicy_AllowNetwork: pszPromiscuousGuestPolicy = Info::tr("allow-vms"); break; + case NetworkAdapterPromiscModePolicy_AllowAll: pszPromiscuousGuestPolicy = Info::tr("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; + case NetworkAdapterType_Am79C960: pszNICType = "Am79C960"; 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 + case NetworkAdapterType_NE1000: pszNICType = "NE1000"; break; + case NetworkAdapterType_NE2000: pszNICType = "NE2000"; break; + case NetworkAdapterType_WD8003: pszNICType = "WD8003"; break; + case NetworkAdapterType_WD8013: pszNICType = "WD8013"; break; + case NetworkAdapterType_ELNK2: pszNICType = "3C503"; break; + case NetworkAdapterType_ELNK1: pszNICType = "3C501"; break; + default: + AssertFailed(); + if (details == VMINFO_MACHINEREADABLE) + pszNICType = "unknown"; + else + pszNICType = Info::tr("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 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(Info::tr("%-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 ? Info::tr("on") : Info::tr("off"), + fTraceEnabled ? Info::tr("on") : Info::tr("off"), + traceFile.isEmpty() ? Bstr(Info::tr("none")).raw() : traceFile.raw(), + pszNICType, + ulLineSpeed / 1000, + (int)ulBootPriority, + pszPromiscuousGuestPolicy, + strBwGroup.isEmpty() ? Bstr(Info::tr("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 = Info::tr("Unknown"); + const char *pszMrHID = "unknown"; + machine->COMGETTER(PointingHIDType)(&aPointingHID); + switch (aPointingHID) + { + case PointingHIDType_None: + pszHID = Info::tr("None"); + pszMrHID = "none"; + break; + case PointingHIDType_PS2Mouse: + pszHID = Info::tr("PS/2 Mouse"); + pszMrHID = "ps2mouse"; + break; + case PointingHIDType_USBMouse: + pszHID = Info::tr("USB Mouse"); + pszMrHID = "usbmouse"; + break; + case PointingHIDType_USBTablet: + pszHID = Info::tr("USB Tablet"); + pszMrHID = "usbtablet"; + break; + case PointingHIDType_ComboMouse: + pszHID = Info::tr("USB Tablet and PS/2 Mouse"); + pszMrHID = "combomouse"; + break; + case PointingHIDType_USBMultiTouch: + pszHID = Info::tr("USB Multi-Touch"); + pszMrHID = "usbmultitouch"; + break; + default: + break; + } + SHOW_UTF8_STRING("hidpointing", Info::tr("Pointing Device:"), details == VMINFO_MACHINEREADABLE ? pszMrHID : pszHID); + + /* Keyboard device information */ + KeyboardHIDType_T aKeyboardHID; + machine->COMGETTER(KeyboardHIDType)(&aKeyboardHID); + pszHID = Info::tr("Unknown"); + pszMrHID = "unknown"; + switch (aKeyboardHID) + { + case KeyboardHIDType_None: + pszHID = Info::tr("None"); + pszMrHID = "none"; + break; + case KeyboardHIDType_PS2Keyboard: + pszHID = Info::tr("PS/2 Keyboard"); + pszMrHID = "ps2kbd"; + break; + case KeyboardHIDType_USBKeyboard: + pszHID = Info::tr("USB Keyboard"); + pszMrHID = "usbkbd"; + break; + case KeyboardHIDType_ComboKeyboard: + pszHID = Info::tr("USB and PS/2 Keyboard"); + pszMrHID = "combokbd"; + break; + default: + break; + } + SHOW_UTF8_STRING("hidkeyboard", Info::tr("Keyboard Device:"), details == VMINFO_MACHINEREADABLE ? pszMrHID : pszHID); + + ComPtr 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 uart; + hrc = machine->GetSerialPort(currentUART, uart.asOutParam()); + if (SUCCEEDED(hrc) && uart) + { + FmtNm(szNm, details == VMINFO_MACHINEREADABLE ? "uart%u" : Info::tr("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(Info::tr("%-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(Info::tr("%-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(Info::tr(", disconnected")); + break; + case PortMode_RawFile: + if (details == VMINFO_MACHINEREADABLE) + RTPrintf("uartmode%d=\"file,%ls\"\n", currentUART + 1, + path.raw()); + else + RTPrintf(Info::tr(", 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(Info::tr(", attached to tcp (%s) '%ls'"), + fServer ? Info::tr("server") : Info::tr("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(Info::tr(", attached to pipe (%s) '%ls'"), + fServer ? Info::tr("server") : Info::tr("client"), path.raw()); + break; + case PortMode_HostDevice: + if (details == VMINFO_MACHINEREADABLE) + RTPrintf("uartmode%d=\"%ls\"\n", currentUART + 1, + path.raw()); + else + RTPrintf(Info::tr(", 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 lpt; + hrc = machine->GetParallelPort(currentLPT, lpt.asOutParam()); + if (SUCCEEDED(hrc) && lpt) + { + FmtNm(szNm, details == VMINFO_MACHINEREADABLE ? "lpt%u" : Info::tr("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(Info::tr("%-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(Info::tr("%-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(Info::tr(", attached to device '%ls'\n"), path.raw()); + } + } + } + + ComPtr audioSettings; + ComPtr audioAdapter; + hrc = machine->COMGETTER(AudioSettings)(audioSettings.asOutParam()); + if (SUCCEEDED(hrc)) + hrc = audioSettings->COMGETTER(Adapter)(audioAdapter.asOutParam()); + if (SUCCEEDED(hrc)) + { + const char *pszDrv = Info::tr("Unknown"); + const char *pszCtrl = Info::tr("Unknown"); + const char *pszCodec = Info::tr("Unknown"); + BOOL fEnabled; + hrc = audioAdapter->COMGETTER(Enabled)(&fEnabled); + if (SUCCEEDED(hrc) && fEnabled) + { + AudioDriverType_T enmDrvType; + hrc = audioAdapter->COMGETTER(AudioDriver)(&enmDrvType); + switch (enmDrvType) + { + case AudioDriverType_Default: + if (details == VMINFO_MACHINEREADABLE) + pszDrv = "default"; + else + pszDrv = Info::tr("Default"); + break; + case AudioDriverType_Null: + if (details == VMINFO_MACHINEREADABLE) + pszDrv = "null"; + else + pszDrv = Info::tr("Null"); + 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_WinMM: + if (details == VMINFO_MACHINEREADABLE) + pszDrv = "winmm"; + else + pszDrv = "WINMM"; + break; + case AudioDriverType_DirectSound: + if (details == VMINFO_MACHINEREADABLE) + pszDrv = "dsound"; + else + pszDrv = "DirectSound"; + break; + case AudioDriverType_WAS: + if (details == VMINFO_MACHINEREADABLE) + pszDrv = "was"; + else + pszDrv = "Windows Audio Session (WAS)"; + 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; + hrc = 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; + hrc = 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", Info::tr("Audio:"), fEnabled ? Info::tr("enabled") : Info::tr("disabled")); + if (fEnabled) + RTPrintf(Info::tr(" (Driver: %s, Controller: %s, Codec: %s)"), pszDrv, pszCtrl, pszCodec); + RTPrintf("\n"); + } + SHOW_BOOLEAN_PROP(audioAdapter, EnabledOut, "audio_out", Info::tr("Audio playback:")); + SHOW_BOOLEAN_PROP(audioAdapter, EnabledIn, "audio_in", Info::tr("Audio capture:")); + + /** @todo Add printing run-time host audio device selection(s) here. */ + } + + /* Shared clipboard */ + { + const char *psz; + ClipboardMode_T enmMode = (ClipboardMode_T)0; + hrc = machine->COMGETTER(ClipboardMode)(&enmMode); + switch (enmMode) + { + case ClipboardMode_Disabled: + psz = "disabled"; + break; + case ClipboardMode_HostToGuest: + psz = details == VMINFO_MACHINEREADABLE ? "hosttoguest" : Info::tr("HostToGuest"); + break; + case ClipboardMode_GuestToHost: + psz = details == VMINFO_MACHINEREADABLE ? "guesttohost" : Info::tr("GuestToHost"); + break; + case ClipboardMode_Bidirectional: + psz = details == VMINFO_MACHINEREADABLE ? "bidirectional" : Info::tr("Bidirectional"); + break; + default: + psz = details == VMINFO_MACHINEREADABLE ? "unknown" : Info::tr("Unknown"); + break; + } + SHOW_UTF8_STRING("clipboard", Info::tr("Clipboard Mode:"), psz); +#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS + SHOW_BOOLEAN_PROP(machine, ClipboardFileTransfersEnabled, "clipboard_file_transfers", Info::tr("Clipboard file transfers:")); +#endif + } + + /* Drag and drop */ + { + const char *psz; + DnDMode_T enmMode; + hrc = machine->COMGETTER(DnDMode)(&enmMode); + switch (enmMode) + { + case DnDMode_Disabled: + psz = "disabled"; + break; + case DnDMode_HostToGuest: + psz = details == VMINFO_MACHINEREADABLE ? "hosttoguest" : Info::tr("HostToGuest"); + break; + case DnDMode_GuestToHost: + psz = details == VMINFO_MACHINEREADABLE ? "guesttohost" : Info::tr("GuestToHost"); + break; + case DnDMode_Bidirectional: + psz = details == VMINFO_MACHINEREADABLE ? "bidirectional" : Info::tr("Bidirectional"); + break; + default: + psz = details == VMINFO_MACHINEREADABLE ? "unknown" : Info::tr("Unknown"); + break; + } + SHOW_UTF8_STRING("draganddrop", Info::tr("Drag and drop Mode:"), psz); + } + + { + SessionState_T sessState; + hrc = machine->COMGETTER(SessionState)(&sessState); + if (SUCCEEDED(hrc) && sessState != SessionState_Unlocked) + { + Bstr sessName; + hrc = machine->COMGETTER(SessionName)(sessName.asOutParam()); + if (SUCCEEDED(hrc) && !sessName.isEmpty()) + SHOW_BSTR_STRING("SessionName", Info::tr("Session name:"), sessName); + } + } + + if (pConsole) + { + do + { + ComPtr display; + hrc = pConsole->COMGETTER(Display)(display.asOutParam()); + if (hrc == E_ACCESSDENIED || display.isNull()) + break; /* VM not powered up */ + if (FAILED(hrc)) + { + com::GlueHandleComError(pConsole, "COMGETTER(Display)(display.asOutParam())", hrc, __FILE__, __LINE__); + return hrc; + } + ULONG xRes, yRes, bpp; + LONG xOrigin, yOrigin; + GuestMonitorStatus_T monitorStatus; + hrc = display->GetScreenResolution(0, &xRes, &yRes, &bpp, &xOrigin, &yOrigin, &monitorStatus); + if (hrc == E_ACCESSDENIED) + break; /* VM not powered up */ + if (FAILED(hrc)) + { + com::ErrorInfo info(display, COM_IIDOF(IDisplay)); + GluePrintErrorInfo(info); + return hrc; + } + if (details == VMINFO_MACHINEREADABLE) + RTPrintf("VideoMode=\"%d,%d,%d\"@%d,%d %d\n", xRes, yRes, bpp, xOrigin, yOrigin, monitorStatus); + else + { + const char *pszMonitorStatus = Info::tr("unknown status"); + switch (monitorStatus) + { + case GuestMonitorStatus_Blank: pszMonitorStatus = Info::tr("blank"); break; + case GuestMonitorStatus_Enabled: pszMonitorStatus = Info::tr("enabled"); break; + case GuestMonitorStatus_Disabled: pszMonitorStatus = Info::tr("disabled"); break; + default: break; + } + RTPrintf("%-28s %dx%dx%d at %d,%d %s\n", Info::tr("Video mode:"), xRes, yRes, bpp, xOrigin, yOrigin, pszMonitorStatus); + } + } + while (0); + } + + /* + * Remote Desktop + */ + ComPtr vrdeServer; + hrc = machine->COMGETTER(VRDEServer)(vrdeServer.asOutParam()); + if (SUCCEEDED(hrc) && 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: + if (details == VMINFO_MACHINEREADABLE) + strAuthType = "null"; + else + strAuthType = Info::tr("null"); + break; + case AuthType_External: + if (details == VMINFO_MACHINEREADABLE) + strAuthType = "external"; + else + strAuthType = Info::tr("external"); + break; + case AuthType_Guest: + if (details == VMINFO_MACHINEREADABLE) + strAuthType = "guest"; + else + strAuthType = Info::tr("guest"); + break; + default: + if (details == VMINFO_MACHINEREADABLE) + strAuthType = "unknown"; + else + strAuthType = Info::tr("unknown"); + break; + } + if (pConsole) + { + ComPtr vrdeServerInfo; + CHECK_ERROR_RET(pConsole, COMGETTER(VRDEServerInfo)(vrdeServerInfo.asOutParam()), hrc); + if (!vrdeServerInfo.isNull()) + { + hrc = vrdeServerInfo->COMGETTER(Port)(¤tPort); + if (hrc == E_ACCESSDENIED) + { + currentPort = -1; /* VM not powered up */ + } + else if (FAILED(hrc)) + { + com::ErrorInfo info(vrdeServerInfo, COM_IIDOF(IVRDEServerInfo)); + GluePrintErrorInfo(info); + return hrc; + } + } + } + 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(Info::tr("%-28s enabled (Address %ls, Ports %ls, MultiConn: %s, ReuseSingleConn: %s, Authentication type: %s)\n"), + "VRDE:", address.raw(), ports.raw(), fMultiCon ? Info::tr("on") : Info::tr("off"), + fReuseCon ? Info::tr("on") : Info::tr("off"), strAuthType); + if (pConsole && currentPort != -1 && currentPort != 0) + RTPrintf("%-28s %d\n", Info::tr("VRDE port:"), currentPort); + if (fVideoChannel) + RTPrintf(Info::tr("%-28s enabled (Quality %ls)\n"), Info::tr("Video redirection:"), videoChannelQuality.raw()); + else + RTPrintf(Info::tr("%-28s disabled\n"), Info::tr("Video redirection:")); + } + com::SafeArray 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]=\n", aProperties[i]); + else + RTPrintf("vrdeproperty[%ls]=\"%ls\"\n", aProperties[i], value.raw()); + } + else + { + if (value.isEmpty()) + RTPrintf(Info::tr("%-28s: %-10lS = \n"), Info::tr("VRDE property"), aProperties[i]); + else + RTPrintf("%-28s: %-10lS = \"%ls\"\n", Info::tr("VRDE property"), aProperties[i], value.raw()); + } + } + } + } + else + { + if (details == VMINFO_MACHINEREADABLE) + RTPrintf("vrde=\"off\"\n"); + else + RTPrintf(Info::tr("%-28s disabled\n"), "VRDE:"); + } + } + + /* + * USB. + */ + SafeIfaceArray USBCtlColl; + hrc = machine->COMGETTER(USBControllers)(ComSafeArrayAsOutParam(USBCtlColl)); + if (SUCCEEDED(hrc)) + { + bool fOhciEnabled = false; + bool fEhciEnabled = false; + bool fXhciEnabled = false; + + for (unsigned i = 0; i < USBCtlColl.size(); i++) + { + USBControllerType_T enmType; + + hrc = USBCtlColl[i]->COMGETTER(Type)(&enmType); + if (SUCCEEDED(hrc)) + { + 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 USBFlts; + hrc = machine->COMGETTER(USBDeviceFilters)(USBFlts.asOutParam()); + if (SUCCEEDED(hrc)) + { + SafeIfaceArray Coll; + hrc = USBFlts->COMGETTER(DeviceFilters)(ComSafeArrayAsOutParam(Coll)); + if (SUCCEEDED(hrc)) + { + if (Coll.size() > 0) + { + if (details != VMINFO_MACHINEREADABLE) + RTPrintf(Info::tr("USB Device Filters:\n")); + for (size_t index = 0; index < Coll.size(); ++index) + { + ComPtr DevPtr = Coll[index]; + + if (details != VMINFO_MACHINEREADABLE) + SHOW_UTF8_STRING("index", Info::tr("Index:"), FmtNm(szNm, "%zu", index)); + SHOW_BOOLEAN_PROP_EX(DevPtr, Active, FmtNm(szNm, "USBFilterActive%zu", index + 1), Info::tr(" Active:"), Info::tr("yes"), Info::tr("no")); + SHOW_STRING_PROP(DevPtr, Name, FmtNm(szNm, "USBFilterName%zu", index + 1), Info::tr(" Name:")); + SHOW_STRING_PROP(DevPtr, VendorId, FmtNm(szNm, "USBFilterVendorId%zu", index + 1), Info::tr(" VendorId:")); + SHOW_STRING_PROP(DevPtr, ProductId, FmtNm(szNm, "USBFilterProductId%zu", index + 1), Info::tr(" ProductId:")); + SHOW_STRING_PROP(DevPtr, Revision, FmtNm(szNm, "USBFilterRevision%zu", index + 1), Info::tr(" Revision:")); + SHOW_STRING_PROP(DevPtr, Manufacturer, FmtNm(szNm, "USBFilterManufacturer%zu", index + 1), Info::tr(" Manufacturer:")); + SHOW_STRING_PROP(DevPtr, Product, FmtNm(szNm, "USBFilterProduct%zu", index + 1), Info::tr(" Product:")); + SHOW_STRING_PROP(DevPtr, Remote, FmtNm(szNm, "USBFilterRemote%zu", index + 1), Info::tr(" Remote:")); + SHOW_STRING_PROP(DevPtr, SerialNumber, FmtNm(szNm, "USBFilterSerialNumber%zu", index + 1), Info::tr(" Serial Number:")); + if (details != VMINFO_MACHINEREADABLE) + { + ULONG fMaskedIfs; + CHECK_ERROR_RET(DevPtr, COMGETTER(MaskedInterfaces)(&fMaskedIfs), hrc); + if (fMaskedIfs) + RTPrintf("%-28s %#010x\n", Info::tr("Masked Interfaces:"), fMaskedIfs); + } + } + } + else if (details != VMINFO_MACHINEREADABLE) + RTPrintf("%-28s %s\n", Info::tr("USB Device Filters:"), Info::tr("")); + } + + if (pConsole) + { + { + SafeIfaceArray coll; + CHECK_ERROR_RET(pConsole, COMGETTER(RemoteUSBDevices)(ComSafeArrayAsOutParam(coll)), hrc); + hrc = showUsbDevices(coll, "USBRemote", Info::tr("Available remote USB devices:"), details); + if (FAILED(hrc)) + return hrc; + } + + { + SafeIfaceArray coll; + CHECK_ERROR_RET(pConsole, COMGETTER(USBDevices)(ComSafeArrayAsOutParam(coll)), hrc); + showUsbDevices(coll, "USBAttach", Info::tr("Currently attached USB devices:"), details); + if (FAILED(hrc)) + return hrc; + } + } + } /* USB */ + +#ifdef VBOX_WITH_PCI_PASSTHROUGH + /* Host PCI passthrough devices */ + { + SafeIfaceArray assignments; + hrc = machine->COMGETTER(PCIDeviceAssignments)(ComSafeArrayAsOutParam(assignments)); + if (SUCCEEDED(hrc)) + { + if (assignments.size() > 0 && (details != VMINFO_MACHINEREADABLE)) + { + RTPrintf(Info::tr("\nAttached physical PCI devices:\n\n")); + } + + for (size_t index = 0; index < assignments.size(); ++index) + { + ComPtr 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(Info::tr(" 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("%-28s ", Info::tr("Bandwidth groups:")); + { + ComPtr bwCtrl; + CHECK_ERROR_RET(machine, COMGETTER(BandwidthControl)(bwCtrl.asOutParam()), hrc); + + hrc = showBandwidthGroups(bwCtrl, details); + } + + + /* + * Shared folders + */ + if (details != VMINFO_MACHINEREADABLE) + RTPrintf("%-28s ", Info::tr("Shared folders:")); + uint32_t numSharedFolders = 0; +#if 0 // not yet implemented + /* globally shared folders first */ + { + SafeIfaceArray sfColl; + CHECK_ERROR_RET(pVirtualBox, COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(sfColl)), rc); + for (size_t i = 0; i < sfColl.size(); ++i) + { + ComPtr sf = sfColl[i]; + showSharedFolder(sf, details, Info::tr("global mapping"), "GlobalMapping", i + 1, numSharedFolders == 0); + ++numSharedFolders; + } + } +#endif + /* now VM mappings */ + { + com::SafeIfaceArray folders; + CHECK_ERROR_RET(machine, COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders)), hrc); + for (size_t i = 0; i < folders.size(); ++i) + { + ComPtr sf = folders[i]; + showSharedFolder(sf, details, Info::tr("machine mapping"), "MachineMapping", i + 1, numSharedFolders == 0); + ++numSharedFolders; + } + } + /* transient mappings */ + if (pConsole) + { + com::SafeIfaceArray folders; + CHECK_ERROR_RET(pConsole, COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders)), hrc); + for (size_t i = 0; i < folders.size(); ++i) + { + ComPtr sf = folders[i]; + showSharedFolder(sf, details, Info::tr("transient mapping"), "TransientMapping", i + 1, numSharedFolders == 0); + ++numSharedFolders; + } + } + if (details != VMINFO_MACHINEREADABLE) + { + if (!numSharedFolders) + RTPrintf(Info::tr("\n")); + else + RTPrintf("\n"); + } + + if (pConsole) + { + /* + * Live VRDE info. + */ + ComPtr vrdeServerInfo; + CHECK_ERROR_RET(pConsole, COMGETTER(VRDEServerInfo)(vrdeServerInfo.asOutParam()), hrc); + 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), hrc); + CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(NumberOfClients)(&cNumberOfClients), hrc); + CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(BeginTime)(&BeginTime), hrc); + CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(EndTime)(&EndTime), hrc); + CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(BytesSent)(&BytesSent), hrc); + CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(BytesSentTotal)(&BytesSentTotal), hrc); + CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(BytesReceived)(&BytesReceived), hrc); + CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(BytesReceivedTotal)(&BytesReceivedTotal), hrc); + CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(User)(User.asOutParam()), hrc); + CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(Domain)(Domain.asOutParam()), hrc); + CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(ClientName)(ClientName.asOutParam()), hrc); + CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(ClientIP)(ClientIP.asOutParam()), hrc); + CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(ClientVersion)(&ClientVersion), hrc); + CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(EncryptionStyle)(&EncryptionStyle), hrc); + } + + SHOW_BOOL_VALUE_EX("VRDEActiveConnection", Info::tr("VRDE Connection:"), fActive, Info::tr("active"), Info::tr("not active")); + SHOW_ULONG_VALUE("VRDEClients=", Info::tr("Clients so far:"), cNumberOfClients, ""); + + if (cNumberOfClients > 0) + { + char szTimeValue[128]; + makeTimeStr(szTimeValue, sizeof(szTimeValue), BeginTime); + if (fActive) + SHOW_UTF8_STRING("VRDEStartTime", Info::tr("Start time:"), szTimeValue); + else + { + SHOW_UTF8_STRING("VRDELastStartTime", Info::tr("Last started:"), szTimeValue); + makeTimeStr(szTimeValue, sizeof(szTimeValue), EndTime); + SHOW_UTF8_STRING("VRDELastEndTime", Info::tr("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", Info::tr("Sent:"), BytesSent, Info::tr("Bytes")); + SHOW_LONG64_VALUE("VRDEThroughputSend", Info::tr("Average speed:"), ThroughputSend, Info::tr("B/s")); + SHOW_LONG64_VALUE("VRDEBytesSentTotal", Info::tr("Sent total:"), BytesSentTotal, Info::tr("Bytes")); + + SHOW_LONG64_VALUE("VRDEBytesReceived", Info::tr("Received:"), BytesReceived, Info::tr("Bytes")); + SHOW_LONG64_VALUE("VRDEThroughputReceive", Info::tr("Speed:"), ThroughputReceive, Info::tr("B/s")); + SHOW_LONG64_VALUE("VRDEBytesReceivedTotal", Info::tr("Received total:"), BytesReceivedTotal, Info::tr("Bytes")); + + if (fActive) + { + SHOW_BSTR_STRING("VRDEUserName", Info::tr("User name:"), User); + SHOW_BSTR_STRING("VRDEDomain", Info::tr("Domain:"), Domain); + SHOW_BSTR_STRING("VRDEClientName", Info::tr("Client name:"), ClientName); + SHOW_BSTR_STRING("VRDEClientIP", Info::tr("Client IP:"), ClientIP); + SHOW_ULONG_VALUE("VRDEClientVersion", Info::tr("Client version:"), ClientVersion, ""); + SHOW_UTF8_STRING("VRDEEncryption", Info::tr("Encryption:"), EncryptionStyle == 0 ? "RDP4" : "RDP5 (X.509)"); + } + } + } + +#ifdef VBOX_WITH_RECORDING + { + ComPtr recordingSettings; + CHECK_ERROR_RET(machine, COMGETTER(RecordingSettings)(recordingSettings.asOutParam()), hrc); + + BOOL fEnabled; + CHECK_ERROR_RET(recordingSettings, COMGETTER(Enabled)(&fEnabled), hrc); + SHOW_BOOL_VALUE_EX("recording_enabled", Info::tr("Recording enabled:"), fEnabled, Info::tr("yes"), Info::tr("no")); + + SafeIfaceArray saScreenSettings; + CHECK_ERROR_RET(recordingSettings, COMGETTER(Screens)(ComSafeArrayAsOutParam(saScreenSettings)), hrc); + + SHOW_ULONG_VALUE("recording_screens", Info::tr("Recording screens:"), saScreenSettings.size(), ""); + + for (size_t i = 0; i < saScreenSettings.size(); ++i) + { + ComPtr screenSettings = saScreenSettings[i]; + + FmtNm(szNm, details == VMINFO_MACHINEREADABLE ? "rec_screen%zu" : Info::tr("Screen %u:"), i); + RTPrintf(Info::tr(" %s\n"), szNm); + + CHECK_ERROR_RET(screenSettings, COMGETTER(Enabled)(&fEnabled), hrc); + ULONG idScreen; + CHECK_ERROR_RET(screenSettings, COMGETTER(Id)(&idScreen), hrc); + com::SafeArray vecFeatures; + CHECK_ERROR_RET(screenSettings, COMGETTER(Features)(ComSafeArrayAsOutParam(vecFeatures)), hrc); + ULONG Width; + CHECK_ERROR_RET(screenSettings, COMGETTER(VideoWidth)(&Width), hrc); + ULONG Height; + CHECK_ERROR_RET(screenSettings, COMGETTER(VideoHeight)(&Height), hrc); + ULONG Rate; + CHECK_ERROR_RET(screenSettings, COMGETTER(VideoRate)(&Rate), hrc); + ULONG Fps; + CHECK_ERROR_RET(screenSettings, COMGETTER(VideoFPS)(&Fps), hrc); + RecordingDestination_T enmDst; + CHECK_ERROR_RET(screenSettings, COMGETTER(Destination)(&enmDst), hrc); + Bstr bstrFile; + CHECK_ERROR_RET(screenSettings, COMGETTER(Filename)(bstrFile.asOutParam()), hrc); + Bstr bstrOptions; + CHECK_ERROR_RET(screenSettings, COMGETTER(Options)(bstrOptions.asOutParam()), hrc); + + BOOL fRecordVideo = FALSE; +# ifdef VBOX_WITH_AUDIO_RECORDING + BOOL fRecordAudio = FALSE; +# endif + for (size_t f = 0; f < vecFeatures.size(); ++f) + { + if (vecFeatures[f] == RecordingFeature_Video) + fRecordVideo = TRUE; +# ifdef VBOX_WITH_AUDIO_RECORDING + else if (vecFeatures[f] == RecordingFeature_Audio) + fRecordAudio = TRUE; +# endif + } + + SHOW_BOOL_VALUE_EX("rec_screen_enabled", Info::tr(" Enabled:"), fEnabled, + Info::tr("yes"), Info::tr("no")); + SHOW_ULONG_VALUE ("rec_screen_id", Info::tr(" ID:"), idScreen, ""); + SHOW_BOOL_VALUE_EX("rec_screen_video_enabled", Info::tr(" Record video:"), fRecordVideo, + Info::tr("yes"), Info::tr("no")); +# ifdef VBOX_WITH_AUDIO_RECORDING + SHOW_BOOL_VALUE_EX("rec_screen_audio_enabled", Info::tr(" Record audio:"), fRecordAudio, + Info::tr("yes"), Info::tr("no")); +# endif + SHOW_UTF8_STRING("rec_screen_dest", Info::tr(" Destination:"), + enmDst == RecordingDestination_File + ? Info::tr("File") : Info::tr("Unknown")); + /** @todo Implement other destinations. */ + if (enmDst == RecordingDestination_File) + SHOW_BSTR_STRING("rec_screen_dest_filename", Info::tr(" File:"), bstrFile); + + SHOW_BSTR_STRING ("rec_screen_opts", Info::tr(" Options:"), bstrOptions); + + /* Video properties. */ + RTStrPrintf(szValue, sizeof(szValue), "%ux%u", Width, Height); + SHOW_UTF8_STRING ("rec_screen_video_res_xy", Info::tr(" Video dimensions:"), szValue); + SHOW_ULONG_VALUE ("rec_screen_video_rate_kbps", Info::tr(" Video rate:"), Rate, Info::tr("kbps")); + SHOW_ULONG_VALUE ("rec_screen_video_fps", Info::tr(" Video FPS:"), Fps, Info::tr("fps")); + + /** @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(Info::tr("Description:\n%ls\n"), description.raw()); + } + } + + /* VMMDev testing config (extra data) */ + if (details != VMINFO_MACHINEREADABLE) + { + Bstr bstr; + CHECK_ERROR2I_RET(machine, GetExtraData(Bstr("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled").raw(), + bstr.asOutParam()), hrcCheck); + int const fEnabled = parseCfgmBool(&bstr); + + CHECK_ERROR2I_RET(machine, GetExtraData(Bstr("VBoxInternal/Devices/VMMDev/0/Config/TestingMMIO").raw(), + bstr.asOutParam()), hrcCheck); + int const fMmio = parseCfgmBool(&bstr); + if (fEnabled || fMmio) + { + RTPrintf("%-28s %s, %s %s\n", + Info::tr("VMMDev Testing"), + fEnabled > 0 ? Info::tr("enabled") : fEnabled == 0 ? Info::tr("disabled") : Info::tr("misconfigured"), + "MMIO:", + fMmio > 0 ? Info::tr("enabled") : fMmio == 0 ? Info::tr("disabled") : Info::tr("misconfigured")); + for (uint32_t i = 0; i < 10; i++) + { + BstrFmt bstrName("VBoxInternal/Devices/VMMDev/0/Config/TestingCfgDword%u", i); + CHECK_ERROR2I_RET(machine, GetExtraData(bstrName.raw(), bstr.asOutParam()), hrcCheck); + if (bstr.isNotEmpty()) + RTPrintf("%-28s %ls\n", FmtNm(szNm, "VMMDev Testing Cfg Dword%u:", i), bstr.raw()); + } + } + } + + /* + * Snapshots. + */ + ComPtr snapshot; + hrc = machine->FindSnapshot(Bstr().raw(), snapshot.asOutParam()); + if (SUCCEEDED(hrc) && snapshot) + { + ComPtr currentSnapshot; + hrc = machine->COMGETTER(CurrentSnapshot)(currentSnapshot.asOutParam()); + if (SUCCEEDED(hrc)) + { + if (details != VMINFO_MACHINEREADABLE) + RTPrintf(Info::tr("* Snapshots:\n")); + showSnapshots(snapshot, currentSnapshot, details); + } + } + + /* + * Guest stuff (mainly interesting when running). + */ + if (details != VMINFO_MACHINEREADABLE) + RTPrintf(Info::tr("* Guest:\n")); + + SHOW_ULONG_PROP(machine, MemoryBalloonSize, "GuestMemoryBalloon", + Info::tr("Configured memory balloon:"), Info::tr("MB")); + + if (pConsole) + { + ComPtr guest; + hrc = pConsole->COMGETTER(Guest)(guest.asOutParam()); + if (SUCCEEDED(hrc) && !guest.isNull()) + { + SHOW_STRING_PROP_NOT_EMPTY(guest, OSTypeId, "GuestOSType", Info::tr("OS type:")); + + AdditionsRunLevelType_T guestRunLevel; /** @todo Add a runlevel-to-string (e.g. 0 = "None") method? */ + hrc = guest->COMGETTER(AdditionsRunLevel)(&guestRunLevel); + if (SUCCEEDED(hrc)) + SHOW_ULONG_VALUE("GuestAdditionsRunLevel", Info::tr("Additions run level:"), (ULONG)guestRunLevel, ""); + + Bstr guestString; + hrc = guest->COMGETTER(AdditionsVersion)(guestString.asOutParam()); + if ( SUCCEEDED(hrc) + && !guestString.isEmpty()) + { + ULONG uRevision; + hrc = guest->COMGETTER(AdditionsRevision)(&uRevision); + if (FAILED(hrc)) + uRevision = 0; + RTStrPrintf(szValue, sizeof(szValue), "%ls r%u", guestString.raw(), uRevision); + SHOW_UTF8_STRING("GuestAdditionsVersion", Info::tr("Additions version:"), szValue); + } + + /* Print information about known Guest Additions facilities: */ + SafeIfaceArray collFac; + CHECK_ERROR_RET(guest, COMGETTER(Facilities)(ComSafeArrayAsOutParam(collFac)), hrc); + if (collFac.size() > 0) + { + if (details != VMINFO_MACHINEREADABLE) + RTPrintf("%s\n", Info::tr("Guest Facilities:")); + LONG64 lLastUpdatedMS; + char szLastUpdated[32]; + AdditionsFacilityStatus_T curStatus; + for (size_t index = 0; index < collFac.size(); ++index) + { + ComPtr fac = collFac[index]; + if (fac) + { + CHECK_ERROR_RET(fac, COMGETTER(Name)(guestString.asOutParam()), hrc); + if (!guestString.isEmpty()) + { + CHECK_ERROR_RET(fac, COMGETTER(Status)(&curStatus), hrc); + CHECK_ERROR_RET(fac, COMGETTER(LastUpdated)(&lLastUpdatedMS), hrc); + if (details == VMINFO_MACHINEREADABLE) + RTPrintf("GuestAdditionsFacility_%ls=%u,%lld\n", + guestString.raw(), curStatus, lLastUpdatedMS); + else + { + makeTimeStr(szLastUpdated, sizeof(szLastUpdated), lLastUpdatedMS); + RTPrintf(Info::tr("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")); + } + } + else if (details != VMINFO_MACHINEREADABLE) + RTPrintf("%-28s %s\n", Info::tr("Guest Facilities:"), Info::tr("")); + } + } + + 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 }, + { "--password-id", 'i', RTGETOPT_REQ_STRING }, + { "-password-id", 'i', RTGETOPT_REQ_STRING }, + { "--password", 'w', RTGETOPT_REQ_STRING }, + { "-password", 'w', RTGETOPT_REQ_STRING }, +}; + +RTEXITCODE handleShowVMInfo(HandlerArg *a) +{ + HRESULT hrc; + const char *VMNameOrUuid = NULL; + bool fLog = false; + uint32_t uLogIdx = 0; + bool fDetails = false; + bool fMachinereadable = false; + Bstr bstrPasswordId; + const char *pszPassword = 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_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 'i': // --password-id + bstrPasswordId = ValueUnion.psz; + break; + + case 'w': // --password + pszPassword = ValueUnion.psz; + break; + + case VINF_GETOPT_NOT_OPTION: + if (!VMNameOrUuid) + VMNameOrUuid = ValueUnion.psz; + else + return errorSyntax(Info::tr("Invalid parameter '%s'"), ValueUnion.psz); + break; + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + /* check for required options */ + if (!VMNameOrUuid) + return errorSyntax(Info::tr("VM name or UUID required")); + + /* try to find the given machine */ + ComPtr machine; + CHECK_ERROR(a->virtualBox, FindMachine(Bstr(VMNameOrUuid).raw(), + machine.asOutParam())); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + /* Printing the log is exclusive. */ + if (fLog && (fMachinereadable || fDetails)) + return errorSyntax(Info::tr("Option --log is exclusive")); + + /* add VM password if required */ + if (pszPassword && bstrPasswordId.isNotEmpty()) + { + Utf8Str strPassword; + if (!RTStrCmp(pszPassword, "-")) + { + /* Get password from console. */ + RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Enter the password:"); + if (rcExit == RTEXITCODE_FAILURE) + return rcExit; + } + else + { + RTEXITCODE rcExit = readPasswordFile(pszPassword, &strPassword); + if (rcExit == RTEXITCODE_FAILURE) + { + RTMsgError("Failed to read new password from file"); + return rcExit; + } + } + CHECK_ERROR(machine, AddEncryptionPassword(bstrPasswordId.raw(), Bstr(strPassword).raw())); + } + + if (fLog) + { + ULONG64 uOffset = 0; + SafeArray 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 */ + hrc = machine->LockMachine(a->session, LockType_Shared); + if (SUCCEEDED(hrc)) + /* get the session machine */ + hrc = a->session->COMGETTER(Machine)(machine.asOutParam()); + + hrc = showVMInfo(a->virtualBox, machine, a->session, details); + + a->session->UnlockMachine(); + } + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +/* 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..f2ed947d --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageList.cpp @@ -0,0 +1,2434 @@ +/* $Id: VBoxManageList.cpp $ */ +/** @file + * VBoxManage - The 'list' command. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "VBoxManage.h" +using namespace com; + +DECLARE_TRANSLATION_CONTEXT(List); + +#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 List::tr("Unknown"); +#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK + case HostNetworkInterfaceMediumType_32BitHack: break; /* Shut up compiler warnings. */ +#endif + } + return List::tr("unknown"); +} + +static const char *getHostIfStatusText(HostNetworkInterfaceStatus_T enmStatus) +{ + switch (enmStatus) + { + case HostNetworkInterfaceStatus_Up: return List::tr("Up"); + case HostNetworkInterfaceStatus_Down: return List::tr("Down"); + case HostNetworkInterfaceStatus_Unknown: return List::tr("Unknown"); +#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK + case HostNetworkInterfaceStatus_32BitHack: break; /* Shut up compiler warnings. */ +#endif + } + return List::tr("unknown"); +} +#endif /* VBOX_WITH_HOSTNETIF_API */ + +static const char*getDeviceTypeText(DeviceType_T enmType) +{ + switch (enmType) + { + case DeviceType_HardDisk: return List::tr("HardDisk"); + case DeviceType_DVD: return "DVD"; + case DeviceType_Floppy: return List::tr("Floppy"); + /* Make MSC happy */ + case DeviceType_Null: return "Null"; + case DeviceType_Network: return List::tr("Network"); + case DeviceType_USB: return "USB"; + case DeviceType_SharedFolder: return List::tr("SharedFolder"); + case DeviceType_Graphics3D: return List::tr("Graphics3D"); + case DeviceType_End: break; /* Shut up compiler warnings. */ +#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK + case DeviceType_32BitHack: break; /* Shut up compiler warnings. */ +#endif + } + return List::tr("Unknown"); +} + + +/** + * List internal networks. + * + * @returns See produceList. + * @param pVirtualBox Reference to the IVirtualBox smart pointer. + */ +static HRESULT listInternalNetworks(const ComPtr pVirtualBox) +{ + HRESULT hrc; + com::SafeArray internalNetworks; + CHECK_ERROR(pVirtualBox, COMGETTER(InternalNetworks)(ComSafeArrayAsOutParam(internalNetworks))); + for (size_t i = 0; i < internalNetworks.size(); ++i) + { + RTPrintf(List::tr("Name: %ls\n"), internalNetworks[i]); + } + return hrc; +} + + +/** + * 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 pVirtualBox, + bool fIsBridged) +{ + HRESULT hrc; + ComPtr host; + CHECK_ERROR(pVirtualBox, COMGETTER(Host)(host.asOutParam())); + com::SafeIfaceArray 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 networkInterface = hostNetworkInterfaces[i]; +#ifndef VBOX_WITH_HOSTNETIF_API + Bstr interfaceName; + networkInterface->COMGETTER(Name)(interfaceName.asOutParam()); + RTPrintf(List::tr("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(List::tr("Name: %ls\n"), interfaceName.raw()); + Bstr interfaceGuid; + networkInterface->COMGETTER(Id)(interfaceGuid.asOutParam()); + RTPrintf("GUID: %ls\n", interfaceGuid.raw()); + BOOL fDHCPEnabled = FALSE; + networkInterface->COMGETTER(DHCPEnabled)(&fDHCPEnabled); + RTPrintf("DHCP: %s\n", fDHCPEnabled ? List::tr("Enabled") : List::tr("Disabled")); + + Bstr IPAddress; + networkInterface->COMGETTER(IPAddress)(IPAddress.asOutParam()); + RTPrintf(List::tr("IPAddress: %ls\n"), IPAddress.raw()); + Bstr NetworkMask; + networkInterface->COMGETTER(NetworkMask)(NetworkMask.asOutParam()); + RTPrintf(List::tr("NetworkMask: %ls\n"), NetworkMask.raw()); + Bstr IPV6Address; + networkInterface->COMGETTER(IPV6Address)(IPV6Address.asOutParam()); + RTPrintf(List::tr("IPV6Address: %ls\n"), IPV6Address.raw()); + ULONG IPV6NetworkMaskPrefixLength; + networkInterface->COMGETTER(IPV6NetworkMaskPrefixLength)(&IPV6NetworkMaskPrefixLength); + RTPrintf(List::tr("IPV6NetworkMaskPrefixLength: %d\n"), IPV6NetworkMaskPrefixLength); + Bstr HardwareAddress; + networkInterface->COMGETTER(HardwareAddress)(HardwareAddress.asOutParam()); + RTPrintf(List::tr("HardwareAddress: %ls\n"), HardwareAddress.raw()); + HostNetworkInterfaceMediumType_T Type; + networkInterface->COMGETTER(MediumType)(&Type); + RTPrintf(List::tr("MediumType: %s\n"), getHostIfMediumTypeText(Type)); + BOOL fWireless = FALSE; + networkInterface->COMGETTER(Wireless)(&fWireless); + RTPrintf(List::tr("Wireless: %s\n"), fWireless ? List::tr("Yes") : List::tr("No")); + HostNetworkInterfaceStatus_T Status; + networkInterface->COMGETTER(Status)(&Status); + RTPrintf(List::tr("Status: %s\n"), getHostIfStatusText(Status)); + Bstr netName; + networkInterface->COMGETTER(NetworkName)(netName.asOutParam()); + RTPrintf(List::tr("VBoxNetworkName: %ls\n\n"), netName.raw()); +#endif + } + return hrc; +} + + +#ifdef VBOX_WITH_VMNET +/** + * List configured host-only networks. + * + * @returns See produceList. + * @param pVirtualBox Reference to the IVirtualBox smart pointer. + * @param Reserved Placeholder! + */ +static HRESULT listHostOnlyNetworks(const ComPtr pVirtualBox) +{ + HRESULT hrc; + com::SafeIfaceArray hostOnlyNetworks; + CHECK_ERROR(pVirtualBox, COMGETTER(HostOnlyNetworks)(ComSafeArrayAsOutParam(hostOnlyNetworks))); + for (size_t i = 0; i < hostOnlyNetworks.size(); ++i) + { + ComPtr hostOnlyNetwork = hostOnlyNetworks[i]; + Bstr bstrNetworkName; + CHECK_ERROR2I(hostOnlyNetwork, COMGETTER(NetworkName)(bstrNetworkName.asOutParam())); + RTPrintf(List::tr("Name: %ls\n"), bstrNetworkName.raw()); + + Bstr bstr; + CHECK_ERROR(hostOnlyNetwork, COMGETTER(Id)(bstr.asOutParam())); + RTPrintf("GUID: %ls\n\n", bstr.raw()); + + BOOL fEnabled = FALSE; + CHECK_ERROR2I(hostOnlyNetwork, COMGETTER(Enabled)(&fEnabled)); + RTPrintf(List::tr("State: %s\n"), fEnabled ? List::tr("Enabled") : List::tr("Disabled")); + + CHECK_ERROR2I(hostOnlyNetwork, COMGETTER(NetworkMask)(bstr.asOutParam())); + RTPrintf(List::tr("NetworkMask: %ls\n"), bstr.raw()); + + CHECK_ERROR2I(hostOnlyNetwork, COMGETTER(LowerIP)(bstr.asOutParam())); + RTPrintf(List::tr("LowerIP: %ls\n"), bstr.raw()); + + CHECK_ERROR2I(hostOnlyNetwork, COMGETTER(UpperIP)(bstr.asOutParam())); + RTPrintf(List::tr("UpperIP: %ls\n"), bstr.raw()); + + // CHECK_ERROR2I(hostOnlyNetwork, COMGETTER(Id)(bstr.asOutParam()); + // RTPrintf("NetworkId: %ls\n", bstr.raw()); + + RTPrintf(List::tr("VBoxNetworkName: hostonly-%ls\n\n"), bstrNetworkName.raw()); + } + return hrc; +} +#endif /* VBOX_WITH_VMNET */ + + +#ifdef VBOX_WITH_CLOUD_NET +/** + * List configured cloud network attachments. + * + * @returns See produceList. + * @param pVirtualBox Reference to the IVirtualBox smart pointer. + * @param Reserved Placeholder! + */ +static HRESULT listCloudNetworks(const ComPtr pVirtualBox) +{ + com::SafeIfaceArray cloudNetworks; + CHECK_ERROR2I_RET(pVirtualBox, COMGETTER(CloudNetworks)(ComSafeArrayAsOutParam(cloudNetworks)), hrcCheck); + for (size_t i = 0; i < cloudNetworks.size(); ++i) + { + ComPtr cloudNetwork = cloudNetworks[i]; + Bstr networkName; + cloudNetwork->COMGETTER(NetworkName)(networkName.asOutParam()); + RTPrintf(List::tr("Name: %ls\n"), networkName.raw()); + // Guid interfaceGuid; + // cloudNetwork->COMGETTER(Id)(interfaceGuid.asOutParam()); + // RTPrintf("GUID: %ls\n\n", Bstr(interfaceGuid.toString()).raw()); + BOOL fEnabled = FALSE; + cloudNetwork->COMGETTER(Enabled)(&fEnabled); + RTPrintf(List::tr("State: %s\n"), fEnabled ? List::tr("Enabled") : List::tr("Disabled")); + + Bstr Provider; + cloudNetwork->COMGETTER(Provider)(Provider.asOutParam()); + RTPrintf(List::tr("CloudProvider: %ls\n"), Provider.raw()); + Bstr Profile; + cloudNetwork->COMGETTER(Profile)(Profile.asOutParam()); + RTPrintf(List::tr("CloudProfile: %ls\n"), Profile.raw()); + Bstr NetworkId; + cloudNetwork->COMGETTER(NetworkId)(NetworkId.asOutParam()); + RTPrintf(List::tr("CloudNetworkId: %ls\n"), NetworkId.raw()); + Bstr netName = BstrFmt("cloud-%ls", networkName.raw()); + RTPrintf(List::tr("VBoxNetworkName: %ls\n\n"), netName.raw()); + } + return S_OK; +} +#endif /* VBOX_WITH_CLOUD_NET */ + + +/** + * List host information. + * + * @returns See produceList. + * @param pVirtualBox Reference to the IVirtualBox smart pointer. + */ +static HRESULT listHostInfo(const ComPtr pVirtualBox) +{ + static struct + { + ProcessorFeature_T feature; + const char *pszName; + } features[] + = + { + { ProcessorFeature_HWVirtEx, List::tr("HW virtualization") }, + { ProcessorFeature_PAE, "PAE" }, + { ProcessorFeature_LongMode, List::tr("long mode") }, + { ProcessorFeature_NestedPaging, List::tr("nested paging") }, + { ProcessorFeature_UnrestrictedGuest, List::tr("unrestricted guest") }, + { ProcessorFeature_NestedHWVirt, List::tr("nested HW virtualization") }, + { ProcessorFeature_VirtVmsaveVmload, List::tr("virt. vmsave/vmload") }, + }; + HRESULT hrc; + ComPtr Host; + CHECK_ERROR(pVirtualBox, COMGETTER(Host)(Host.asOutParam())); + + RTPrintf(List::tr("Host Information:\n\n")); + + LONG64 u64UtcTime = 0; + CHECK_ERROR(Host, COMGETTER(UTCTime)(&u64UtcTime)); + RTTIMESPEC timeSpec; + char szTime[32]; + RTPrintf(List::tr("Host time: %s\n"), RTTimeSpecToString(RTTimeSpecSetMilli(&timeSpec, u64UtcTime), szTime, sizeof(szTime))); + + ULONG processorOnlineCount = 0; + CHECK_ERROR(Host, COMGETTER(ProcessorOnlineCount)(&processorOnlineCount)); + RTPrintf(List::tr("Processor online count: %lu\n"), processorOnlineCount); + ULONG processorCount = 0; + CHECK_ERROR(Host, COMGETTER(ProcessorCount)(&processorCount)); + RTPrintf(List::tr("Processor count: %lu\n"), processorCount); + ULONG processorOnlineCoreCount = 0; + CHECK_ERROR(Host, COMGETTER(ProcessorOnlineCoreCount)(&processorOnlineCoreCount)); + RTPrintf(List::tr("Processor online core count: %lu\n"), processorOnlineCoreCount); + ULONG processorCoreCount = 0; + CHECK_ERROR(Host, COMGETTER(ProcessorCoreCount)(&processorCoreCount)); + RTPrintf(List::tr("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(List::tr("Processor supports %s: %s\n"), features[i].pszName, supported ? List::tr("yes") : List::tr("no")); + } + for (ULONG i = 0; i < processorCount; i++) + { + ULONG processorSpeed = 0; + CHECK_ERROR(Host, GetProcessorSpeed(i, &processorSpeed)); + if (processorSpeed) + RTPrintf(List::tr("Processor#%u speed: %lu MHz\n"), i, processorSpeed); + else + RTPrintf(List::tr("Processor#%u speed: unknown\n"), i); + Bstr processorDescription; + CHECK_ERROR(Host, GetProcessorDescription(i, processorDescription.asOutParam())); + RTPrintf(List::tr("Processor#%u description: %ls\n"), i, processorDescription.raw()); + } + + ULONG memorySize = 0; + CHECK_ERROR(Host, COMGETTER(MemorySize)(&memorySize)); + RTPrintf(List::tr("Memory size: %lu MByte\n", "", memorySize), memorySize); + + ULONG memoryAvailable = 0; + CHECK_ERROR(Host, COMGETTER(MemoryAvailable)(&memoryAvailable)); + RTPrintf(List::tr("Memory available: %lu MByte\n", "", memoryAvailable), memoryAvailable); + + Bstr operatingSystem; + CHECK_ERROR(Host, COMGETTER(OperatingSystem)(operatingSystem.asOutParam())); + RTPrintf(List::tr("Operating system: %ls\n"), operatingSystem.raw()); + + Bstr oSVersion; + CHECK_ERROR(Host, COMGETTER(OSVersion)(oSVersion.asOutParam())); + RTPrintf(List::tr("Operating system version: %ls\n"), oSVersion.raw()); + return hrc; +} + + +/** + * 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 pVirtualBox, + const com::SafeIfaceArray &aMedia, + const char *pszParentUUIDStr, + bool fOptLong) +{ + HRESULT hrc = S_OK; + for (size_t i = 0; i < aMedia.size(); ++i) + { + ComPtr pMedium = aMedia[i]; + + hrc = showMediumInfo(pVirtualBox, pMedium, pszParentUUIDStr, fOptLong); + + RTPrintf("\n"); + + com::SafeIfaceArray 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 + hrc = listMedia(pVirtualBox, children, Utf8Str(uuid).c_str(), fOptLong); + } + } + + return hrc; +} + + +/** + * List virtual image backends. + * + * @returns See produceList. + * @param pVirtualBox Reference to the IVirtualBox smart pointer. + */ +static HRESULT listHddBackends(const ComPtr pVirtualBox) +{ + HRESULT hrc; + ComPtr systemProperties; + CHECK_ERROR(pVirtualBox, COMGETTER(SystemProperties)(systemProperties.asOutParam())); + com::SafeIfaceArray mediumFormats; + CHECK_ERROR(systemProperties, COMGETTER(MediumFormats)(ComSafeArrayAsOutParam(mediumFormats))); + + RTPrintf(List::tr("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 mediumFormatCap; + CHECK_ERROR(mediumFormats[i], + COMGETTER(Capabilities)(ComSafeArrayAsOutParam(mediumFormatCap))); + for (ULONG j = 0; j < mediumFormatCap.size(); j++) + caps |= mediumFormatCap[j]; + + + RTPrintf(List::tr("Backend %u: id='%ls' description='%ls' capabilities=%#06x extensions='"), + i, id.raw(), description.raw(), caps); + + /* File extensions */ + com::SafeArray fileExtensions; + com::SafeArray 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 propertyNames; + com::SafeArray propertyDescriptions; + com::SafeArray propertyTypes; + com::SafeArray propertyFlags; + com::SafeArray propertyDefaults; + CHECK_ERROR(mediumFormats[i], + DescribeProperties(ComSafeArrayAsOutParam(propertyNames), + ComSafeArrayAsOutParam(propertyDescriptions), + ComSafeArrayAsOutParam(propertyTypes), + ComSafeArrayAsOutParam(propertyFlags), + ComSafeArrayAsOutParam(propertyDefaults))); + + RTPrintf(List::tr(" properties=(")); + if (propertyNames.size() > 0) + { + for (size_t j = 0; j < propertyNames.size(); ++j) + { + RTPrintf(List::tr("\n name='%ls' desc='%ls' type="), + Bstr(propertyNames[j]).raw(), Bstr(propertyDescriptions[j]).raw()); + switch (propertyTypes[j]) + { + case DataType_Int32: RTPrintf(List::tr("int")); break; + case DataType_Int8: RTPrintf(List::tr("byte")); break; + case DataType_String: RTPrintf(List::tr("string")); break; +#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK + case DataType_32BitHack: break; /* Shut up compiler warnings. */ +#endif + } + RTPrintf(List::tr(" flags=%#04x"), propertyFlags[j]); + RTPrintf(List::tr(" default='%ls'"), Bstr(propertyDefaults[j]).raw()); + if (j != propertyNames.size()-1) + RTPrintf(", "); + } + } + RTPrintf(")\n"); + } + return hrc; +} + + +/** + * List USB devices attached to the host. + * + * @returns See produceList. + * @param pVirtualBox Reference to the IVirtualBox smart pointer. + */ +static HRESULT listUsbHost(const ComPtr &pVirtualBox) +{ + HRESULT hrc; + ComPtr Host; + CHECK_ERROR_RET(pVirtualBox, COMGETTER(Host)(Host.asOutParam()), 1); + + SafeIfaceArray CollPtr; + CHECK_ERROR_RET(Host, COMGETTER(USBDevices)(ComSafeArrayAsOutParam(CollPtr)), 1); + + RTPrintf(List::tr("Host USB Devices:\n\n")); + + if (CollPtr.size() == 0) + { + RTPrintf(List::tr("\n\n")); + } + else + { + for (size_t i = 0; i < CollPtr.size(); ++i) + { + ComPtr 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); + USBConnectionSpeed_T enmSpeed; + CHECK_ERROR_RET(dev, COMGETTER(Speed)(&enmSpeed), 1); + + RTPrintf(List::tr( + "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 = List::tr("Low"); + break; + case USBConnectionSpeed_Full: + pszSpeed = List::tr("Full"); + break; + case USBConnectionSpeed_High: + pszSpeed = List::tr("High"); + break; + case USBConnectionSpeed_Super: + pszSpeed = List::tr("Super"); + break; + case USBConnectionSpeed_SuperPlus: + pszSpeed = List::tr("SuperPlus"); + break; + default: + ASSERT(false); + break; + } + + RTPrintf(List::tr("USB version/speed: %u/%s\n"), usVersion, pszSpeed); + + /* optional stuff. */ + SafeArray CollDevInfo; + Bstr bstr; + CHECK_ERROR_RET(dev, COMGETTER(DeviceInfo)(ComSafeArrayAsOutParam(CollDevInfo)), 1); + if (CollDevInfo.size() >= 1) + bstr = Bstr(CollDevInfo[0]); + if (!bstr.isEmpty()) + RTPrintf(List::tr("Manufacturer: %ls\n"), bstr.raw()); + if (CollDevInfo.size() >= 2) + bstr = Bstr(CollDevInfo[1]); + if (!bstr.isEmpty()) + RTPrintf(List::tr("Product: %ls\n"), bstr.raw()); + CHECK_ERROR_RET(dev, COMGETTER(SerialNumber)(bstr.asOutParam()), 1); + if (!bstr.isEmpty()) + RTPrintf(List::tr("SerialNumber: %ls\n"), bstr.raw()); + CHECK_ERROR_RET(dev, COMGETTER(Address)(bstr.asOutParam()), 1); + if (!bstr.isEmpty()) + RTPrintf(List::tr("Address: %ls\n"), bstr.raw()); + CHECK_ERROR_RET(dev, COMGETTER(PortPath)(bstr.asOutParam()), 1); + if (!bstr.isEmpty()) + RTPrintf(List::tr("Port path: %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 = List::tr("Not supported"); + break; + case USBDeviceState_Unavailable: + pszState = List::tr("Unavailable"); + break; + case USBDeviceState_Busy: + pszState = List::tr("Busy"); + break; + case USBDeviceState_Available: + pszState = List::tr("Available"); + break; + case USBDeviceState_Held: + pszState = List::tr("Held"); + break; + case USBDeviceState_Captured: + pszState = List::tr("Captured"); + break; + default: + ASSERT(false); + break; + } + RTPrintf(List::tr("Current State: %s\n\n"), pszState); + } + } + return hrc; +} + + +/** + * List USB filters. + * + * @returns See produceList. + * @param pVirtualBox Reference to the IVirtualBox smart pointer. + */ +static HRESULT listUsbFilters(const ComPtr &pVirtualBox) +{ + HRESULT hrc; + + RTPrintf(List::tr("Global USB Device Filters:\n\n")); + + ComPtr host; + CHECK_ERROR_RET(pVirtualBox, COMGETTER(Host)(host.asOutParam()), 1); + + SafeIfaceArray coll; + CHECK_ERROR_RET(host, COMGETTER(USBDeviceFilters)(ComSafeArrayAsOutParam(coll)), 1); + + if (coll.size() == 0) + { + RTPrintf(List::tr("\n\n")); + } + else + { + for (size_t index = 0; index < coll.size(); ++index) + { + ComPtr flt = coll[index]; + + /* Query info. */ + + RTPrintf(List::tr("Index: %zu\n"), index); + + BOOL active = FALSE; + CHECK_ERROR_RET(flt, COMGETTER(Active)(&active), 1); + RTPrintf(List::tr("Active: %s\n"), active ? List::tr("yes") : List::tr("no")); + + USBDeviceFilterAction_T action; + CHECK_ERROR_RET(flt, COMGETTER(Action)(&action), 1); + const char *pszAction = List::tr(""); + switch (action) + { + case USBDeviceFilterAction_Ignore: + pszAction = List::tr("Ignore"); + break; + case USBDeviceFilterAction_Hold: + pszAction = List::tr("Hold"); + break; + default: + break; + } + RTPrintf(List::tr("Action: %s\n"), pszAction); + + Bstr bstr; + CHECK_ERROR_RET(flt, COMGETTER(Name)(bstr.asOutParam()), 1); + RTPrintf(List::tr("Name: %ls\n"), bstr.raw()); + CHECK_ERROR_RET(flt, COMGETTER(VendorId)(bstr.asOutParam()), 1); + RTPrintf(List::tr("VendorId: %ls\n"), bstr.raw()); + CHECK_ERROR_RET(flt, COMGETTER(ProductId)(bstr.asOutParam()), 1); + RTPrintf(List::tr("ProductId: %ls\n"), bstr.raw()); + CHECK_ERROR_RET(flt, COMGETTER(Revision)(bstr.asOutParam()), 1); + RTPrintf(List::tr("Revision: %ls\n"), bstr.raw()); + CHECK_ERROR_RET(flt, COMGETTER(Manufacturer)(bstr.asOutParam()), 1); + RTPrintf(List::tr("Manufacturer: %ls\n"), bstr.raw()); + CHECK_ERROR_RET(flt, COMGETTER(Product)(bstr.asOutParam()), 1); + RTPrintf(List::tr("Product: %ls\n"), bstr.raw()); + CHECK_ERROR_RET(flt, COMGETTER(SerialNumber)(bstr.asOutParam()), 1); + RTPrintf(List::tr("Serial Number: %ls\n"), bstr.raw()); + CHECK_ERROR_RET(flt, COMGETTER(Port)(bstr.asOutParam()), 1); + RTPrintf(List::tr("Port: %ls\n\n"), bstr.raw()); + } + } + return hrc; +} + + +/** + * List system properties. + * + * @returns See produceList. + * @param pVirtualBox Reference to the IVirtualBox smart pointer. + */ +static HRESULT listSystemProperties(const ComPtr &pVirtualBox) +{ + ComPtr 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(List::tr("API version: %ls\n"), str.raw()); + + systemProperties->COMGETTER(MinGuestRAM)(&ulValue); + RTPrintf(List::tr("Minimum guest RAM size: %u Megabytes\n", "", ulValue), ulValue); + systemProperties->COMGETTER(MaxGuestRAM)(&ulValue); + RTPrintf(List::tr("Maximum guest RAM size: %u Megabytes\n", "", ulValue), ulValue); + systemProperties->COMGETTER(MinGuestVRAM)(&ulValue); + RTPrintf(List::tr("Minimum video RAM size: %u Megabytes\n", "", ulValue), ulValue); + systemProperties->COMGETTER(MaxGuestVRAM)(&ulValue); + RTPrintf(List::tr("Maximum video RAM size: %u Megabytes\n", "", ulValue), ulValue); + systemProperties->COMGETTER(MaxGuestMonitors)(&ulValue); + RTPrintf(List::tr("Maximum guest monitor count: %u\n"), ulValue); + systemProperties->COMGETTER(MinGuestCPUCount)(&ulValue); + RTPrintf(List::tr("Minimum guest CPU count: %u\n"), ulValue); + systemProperties->COMGETTER(MaxGuestCPUCount)(&ulValue); + RTPrintf(List::tr("Maximum guest CPU count: %u\n"), ulValue); + systemProperties->COMGETTER(InfoVDSize)(&i64Value); + RTPrintf(List::tr("Virtual disk limit (info): %lld Bytes\n", "" , i64Value), i64Value); + systemProperties->COMGETTER(SerialPortCount)(&ulValue); + RTPrintf(List::tr("Maximum Serial Port count: %u\n"), ulValue); + systemProperties->COMGETTER(ParallelPortCount)(&ulValue); + RTPrintf(List::tr("Maximum Parallel Port count: %u\n"), ulValue); + systemProperties->COMGETTER(MaxBootPosition)(&ulValue); + RTPrintf(List::tr("Maximum Boot Position: %u\n"), ulValue); + systemProperties->GetMaxNetworkAdapters(ChipsetType_PIIX3, &ulValue); + RTPrintf(List::tr("Maximum PIIX3 Network Adapter count: %u\n"), ulValue); + systemProperties->GetMaxNetworkAdapters(ChipsetType_ICH9, &ulValue); + RTPrintf(List::tr("Maximum ICH9 Network Adapter count: %u\n"), ulValue); + systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_PIIX3, StorageBus_IDE, &ulValue); + RTPrintf(List::tr("Maximum PIIX3 IDE Controllers: %u\n"), ulValue); + systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_ICH9, StorageBus_IDE, &ulValue); + RTPrintf(List::tr("Maximum ICH9 IDE Controllers: %u\n"), ulValue); + systemProperties->GetMaxPortCountForStorageBus(StorageBus_IDE, &ulValue); + RTPrintf(List::tr("Maximum IDE Port count: %u\n"), ulValue); + systemProperties->GetMaxDevicesPerPortForStorageBus(StorageBus_IDE, &ulValue); + RTPrintf(List::tr("Maximum Devices per IDE Port: %u\n"), ulValue); + systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_PIIX3, StorageBus_SATA, &ulValue); + RTPrintf(List::tr("Maximum PIIX3 SATA Controllers: %u\n"), ulValue); + systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_ICH9, StorageBus_SATA, &ulValue); + RTPrintf(List::tr("Maximum ICH9 SATA Controllers: %u\n"), ulValue); + systemProperties->GetMaxPortCountForStorageBus(StorageBus_SATA, &ulValue); + RTPrintf(List::tr("Maximum SATA Port count: %u\n"), ulValue); + systemProperties->GetMaxDevicesPerPortForStorageBus(StorageBus_SATA, &ulValue); + RTPrintf(List::tr("Maximum Devices per SATA Port: %u\n"), ulValue); + systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_PIIX3, StorageBus_SCSI, &ulValue); + RTPrintf(List::tr("Maximum PIIX3 SCSI Controllers: %u\n"), ulValue); + systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_ICH9, StorageBus_SCSI, &ulValue); + RTPrintf(List::tr("Maximum ICH9 SCSI Controllers: %u\n"), ulValue); + systemProperties->GetMaxPortCountForStorageBus(StorageBus_SCSI, &ulValue); + RTPrintf(List::tr("Maximum SCSI Port count: %u\n"), ulValue); + systemProperties->GetMaxDevicesPerPortForStorageBus(StorageBus_SCSI, &ulValue); + RTPrintf(List::tr("Maximum Devices per SCSI Port: %u\n"), ulValue); + systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_PIIX3, StorageBus_SAS, &ulValue); + RTPrintf(List::tr("Maximum SAS PIIX3 Controllers: %u\n"), ulValue); + systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_ICH9, StorageBus_SAS, &ulValue); + RTPrintf(List::tr("Maximum SAS ICH9 Controllers: %u\n"), ulValue); + systemProperties->GetMaxPortCountForStorageBus(StorageBus_SAS, &ulValue); + RTPrintf(List::tr("Maximum SAS Port count: %u\n"), ulValue); + systemProperties->GetMaxDevicesPerPortForStorageBus(StorageBus_SAS, &ulValue); + RTPrintf(List::tr("Maximum Devices per SAS Port: %u\n"), ulValue); + systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_PIIX3, StorageBus_PCIe, &ulValue); + RTPrintf(List::tr("Maximum NVMe PIIX3 Controllers: %u\n"), ulValue); + systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_ICH9, StorageBus_PCIe, &ulValue); + RTPrintf(List::tr("Maximum NVMe ICH9 Controllers: %u\n"), ulValue); + systemProperties->GetMaxPortCountForStorageBus(StorageBus_PCIe, &ulValue); + RTPrintf(List::tr("Maximum NVMe Port count: %u\n"), ulValue); + systemProperties->GetMaxDevicesPerPortForStorageBus(StorageBus_PCIe, &ulValue); + RTPrintf(List::tr("Maximum Devices per NVMe Port: %u\n"), ulValue); + systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_PIIX3, StorageBus_VirtioSCSI, &ulValue); + RTPrintf(List::tr("Maximum virtio-scsi PIIX3 Controllers: %u\n"), ulValue); + systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_ICH9, StorageBus_VirtioSCSI, &ulValue); + RTPrintf(List::tr("Maximum virtio-scsi ICH9 Controllers: %u\n"), ulValue); + systemProperties->GetMaxPortCountForStorageBus(StorageBus_VirtioSCSI, &ulValue); + RTPrintf(List::tr("Maximum virtio-scsi Port count: %u\n"), ulValue); + systemProperties->GetMaxDevicesPerPortForStorageBus(StorageBus_VirtioSCSI, &ulValue); + RTPrintf(List::tr("Maximum Devices per virtio-scsi Port: %u\n"), ulValue); + systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_PIIX3, StorageBus_Floppy, &ulValue); + RTPrintf(List::tr("Maximum PIIX3 Floppy Controllers:%u\n"), ulValue); + systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_ICH9, StorageBus_Floppy, &ulValue); + RTPrintf(List::tr("Maximum ICH9 Floppy Controllers: %u\n"), ulValue); + systemProperties->GetMaxPortCountForStorageBus(StorageBus_Floppy, &ulValue); + RTPrintf(List::tr("Maximum Floppy Port count: %u\n"), ulValue); + systemProperties->GetMaxDevicesPerPortForStorageBus(StorageBus_Floppy, &ulValue); + RTPrintf(List::tr("Maximum Devices per Floppy Port: %u\n"), ulValue); +#if 0 + systemProperties->GetFreeDiskSpaceWarning(&i64Value); + RTPrintf(List::tr("Free disk space warning at: %u Bytes\n", "", i64Value), i64Value); + systemProperties->GetFreeDiskSpacePercentWarning(&ulValue); + RTPrintf(List::tr("Free disk space warning at: %u %%\n"), ulValue); + systemProperties->GetFreeDiskSpaceError(&i64Value); + RTPrintf(List::tr("Free disk space error at: %u Bytes\n", "", i64Value), i64Value); + systemProperties->GetFreeDiskSpacePercentError(&ulValue); + RTPrintf(List::tr("Free disk space error at: %u %%\n"), ulValue); +#endif + systemProperties->COMGETTER(DefaultMachineFolder)(str.asOutParam()); + RTPrintf(List::tr("Default machine folder: %ls\n"), str.raw()); + systemProperties->COMGETTER(RawModeSupported)(&fValue); + RTPrintf(List::tr("Raw-mode Supported: %s\n"), fValue ? List::tr("yes") : List::tr("no")); + systemProperties->COMGETTER(ExclusiveHwVirt)(&fValue); + RTPrintf(List::tr("Exclusive HW virtualization use: %s\n"), fValue ? List::tr("on") : List::tr("off")); + systemProperties->COMGETTER(DefaultHardDiskFormat)(str.asOutParam()); + RTPrintf(List::tr("Default hard disk format: %ls\n"), str.raw()); + systemProperties->COMGETTER(VRDEAuthLibrary)(str.asOutParam()); + RTPrintf(List::tr("VRDE auth library: %ls\n"), str.raw()); + systemProperties->COMGETTER(WebServiceAuthLibrary)(str.asOutParam()); + RTPrintf(List::tr("Webservice auth. library: %ls\n"), str.raw()); + systemProperties->COMGETTER(DefaultVRDEExtPack)(str.asOutParam()); + RTPrintf(List::tr("Remote desktop ExtPack: %ls\n"), str.raw()); + systemProperties->COMGETTER(DefaultCryptoExtPack)(str.asOutParam()); + RTPrintf(List::tr("VM encryption ExtPack: %ls\n"), str.raw()); + systemProperties->COMGETTER(LogHistoryCount)(&ulValue); + RTPrintf(List::tr("Log history count: %u\n"), ulValue); + systemProperties->COMGETTER(DefaultFrontend)(str.asOutParam()); + RTPrintf(List::tr("Default frontend: %ls\n"), str.raw()); + AudioDriverType_T enmAudio; + systemProperties->COMGETTER(DefaultAudioDriver)(&enmAudio); + switch (enmAudio) + { + case AudioDriverType_Default: psz = List::tr("Default"); break; + case AudioDriverType_Null: psz = List::tr("Null"); break; + case AudioDriverType_OSS: psz = "OSS"; break; + case AudioDriverType_ALSA: psz = "ALSA"; break; + case AudioDriverType_Pulse: psz = "PulseAudio"; break; + case AudioDriverType_WinMM: psz = "WinMM"; break; + case AudioDriverType_DirectSound: psz = "DirectSound"; break; + case AudioDriverType_WAS: psz = "Windows Audio Session"; break; + case AudioDriverType_CoreAudio: psz = "CoreAudio"; break; + case AudioDriverType_SolAudio: psz = "SolAudio"; break; + case AudioDriverType_MMPM: psz = "MMPM"; break; + default: psz = List::tr("Unknown"); + } + RTPrintf(List::tr("Default audio driver: %s\n"), psz); + systemProperties->COMGETTER(AutostartDatabasePath)(str.asOutParam()); + RTPrintf(List::tr("Autostart database path: %ls\n"), str.raw()); + systemProperties->COMGETTER(DefaultAdditionsISO)(str.asOutParam()); + RTPrintf(List::tr("Default Guest Additions ISO: %ls\n"), str.raw()); + systemProperties->COMGETTER(LoggingLevel)(str.asOutParam()); + RTPrintf(List::tr("Logging Level: %ls\n"), str.raw()); + ProxyMode_T enmProxyMode = (ProxyMode_T)42; + systemProperties->COMGETTER(ProxyMode)(&enmProxyMode); + psz = List::tr("Unknown"); + switch (enmProxyMode) + { + case ProxyMode_System: psz = List::tr("System"); break; + case ProxyMode_NoProxy: psz = List::tr("NoProxy"); break; + case ProxyMode_Manual: psz = List::tr("Manual"); break; +#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK + case ProxyMode_32BitHack: break; /* Shut up compiler warnings. */ +#endif + } + RTPrintf(List::tr("Proxy Mode: %s\n"), psz); + systemProperties->COMGETTER(ProxyURL)(str.asOutParam()); + RTPrintf(List::tr("Proxy URL: %ls\n"), str.raw()); +#ifdef VBOX_WITH_MAIN_NLS + systemProperties->COMGETTER(LanguageId)(str.asOutParam()); + RTPrintf(List::tr("User language: %ls\n"), str.raw()); +#endif + return S_OK; +} + +#ifdef VBOX_WITH_UPDATE_AGENT +static HRESULT listUpdateAgentConfig(ComPtr ptrUpdateAgent) +{ + BOOL fValue; + ptrUpdateAgent->COMGETTER(Enabled)(&fValue); + RTPrintf(List::tr("Enabled: %s\n"), fValue ? List::tr("yes") : List::tr("no")); + ULONG ulValue; + ptrUpdateAgent->COMGETTER(CheckCount)(&ulValue); + RTPrintf(List::tr("Check count: %u\n"), ulValue); + ptrUpdateAgent->COMGETTER(CheckFrequency)(&ulValue); + if (ulValue == 0) + RTPrintf(List::tr("Check frequency: never\n")); + else if (ulValue == 1) + RTPrintf(List::tr("Check frequency: every day\n")); + else + RTPrintf(List::tr("Check frequency: every %u days\n", "", ulValue), ulValue); + + Bstr str; + const char *psz; + UpdateChannel_T enmUpdateChannel; + ptrUpdateAgent->COMGETTER(Channel)(&enmUpdateChannel); + switch (enmUpdateChannel) + { + case UpdateChannel_Stable: + psz = List::tr("Stable: Maintenance and minor releases within the same major release"); + break; + case UpdateChannel_All: + psz = List::tr("All releases: All stable releases, including major versions"); + break; + case UpdateChannel_WithBetas: + psz = List::tr("With Betas: All stable and major releases, including beta versions"); + break; + case UpdateChannel_WithTesting: + psz = List::tr("With Testing: All stable, major and beta releases, including testing versions"); + break; + default: + psz = List::tr("Unset"); + break; + } + RTPrintf(List::tr("Channel: %s\n"), psz); + ptrUpdateAgent->COMGETTER(RepositoryURL)(str.asOutParam()); + RTPrintf(List::tr("Repository: %ls\n"), str.raw()); + ptrUpdateAgent->COMGETTER(LastCheckDate)(str.asOutParam()); + RTPrintf(List::tr("Last check date: %ls\n"), str.raw()); + + return S_OK; +} + +static HRESULT listUpdateAgents(const ComPtr &pVirtualBox) +{ + ComPtr pHost; + CHECK_ERROR2I_RET(pVirtualBox, COMGETTER(Host)(pHost.asOutParam()), RTEXITCODE_FAILURE); + + ComPtr pUpdateHost; + CHECK_ERROR2I_RET(pHost, COMGETTER(UpdateHost)(pUpdateHost.asOutParam()), RTEXITCODE_FAILURE); + /** @todo Add other update agents here. */ + + return listUpdateAgentConfig(pUpdateHost); +} +#endif /* VBOX_WITH_UPDATE_AGENT */ + +/** + * Helper for listDhcpServers() that shows a DHCP configuration. + */ +static HRESULT showDhcpConfig(ComPtr ptrConfig) +{ + HRESULT hrcRet = S_OK; + + ULONG secs = 0; + CHECK_ERROR2I_STMT(ptrConfig, COMGETTER(MinLeaseTime)(&secs), hrcRet = hrcCheck); + if (secs == 0) + RTPrintf(List::tr(" minLeaseTime: default\n")); + else + RTPrintf(List::tr(" minLeaseTime: %u sec\n"), secs); + + secs = 0; + CHECK_ERROR2I_STMT(ptrConfig, COMGETTER(DefaultLeaseTime)(&secs), hrcRet = hrcCheck); + if (secs == 0) + RTPrintf(List::tr(" defaultLeaseTime: default\n")); + else + RTPrintf(List::tr(" defaultLeaseTime: %u sec\n"), secs); + + secs = 0; + CHECK_ERROR2I_STMT(ptrConfig, COMGETTER(MaxLeaseTime)(&secs), hrcRet = hrcCheck); + if (secs == 0) + RTPrintf(List::tr(" maxLeaseTime: default\n")); + else + RTPrintf(List::tr(" maxLeaseTime: %u sec\n"), secs); + + com::SafeArray Options; + HRESULT hrc; + CHECK_ERROR2_STMT(hrc, ptrConfig, COMGETTER(ForcedOptions(ComSafeArrayAsOutParam(Options))), hrcRet = hrc); + if (FAILED(hrc)) + RTPrintf(List::tr(" Forced options: %Rhrc\n"), hrc); + else if (Options.size() == 0) + RTPrintf(List::tr(" Forced options: None\n")); + else + { + RTPrintf(List::tr(" Forced options: ")); + for (size_t i = 0; i < Options.size(); i++) + RTPrintf(i ? ", %u" : "%u", Options[i]); + RTPrintf("\n"); + } + + CHECK_ERROR2_STMT(hrc, ptrConfig, COMGETTER(SuppressedOptions(ComSafeArrayAsOutParam(Options))), hrcRet = hrc); + if (FAILED(hrc)) + RTPrintf(List::tr(" Suppressed opt.s: %Rhrc\n"), hrc); + else if (Options.size() == 0) + RTPrintf(List::tr(" Suppressed opts.: None\n")); + else + { + RTPrintf(List::tr(" Suppressed opts.: ")); + for (size_t i = 0; i < Options.size(); i++) + RTPrintf(i ? ", %u" : "%u", Options[i]); + RTPrintf("\n"); + } + + com::SafeArray Encodings; + com::SafeArray Values; + CHECK_ERROR2_STMT(hrc, ptrConfig, GetAllOptions(ComSafeArrayAsOutParam(Options), + ComSafeArrayAsOutParam(Encodings), + ComSafeArrayAsOutParam(Values)), hrcRet = hrc); + if (FAILED(hrc)) + RTPrintf(List::tr(" DHCP options: %Rhrc\n"), hrc); + else if (Options.size() != Encodings.size() || Options.size() != Values.size()) + { + RTPrintf(List::tr(" DHCP options: Return count mismatch: %zu, %zu, %zu\n"), + Options.size(), Encodings.size(), Values.size()); + hrcRet = E_FAIL; + } + else if (Options.size() == 0) + RTPrintf(List::tr(" DHCP options: None\n")); + else + for (size_t i = 0; i < Options.size(); i++) + { + switch (Encodings[i]) + { + case DHCPOptionEncoding_Normal: + RTPrintf(List::tr(" %3d/legacy: %ls\n"), Options[i], Values[i]); + break; + case DHCPOptionEncoding_Hex: + RTPrintf(" %3d/hex: %ls\n", Options[i], Values[i]); + break; + default: + RTPrintf(" %3d/%u?: %ls\n", Options[i], Encodings[i], Values[i]); + break; + } + } + + return S_OK; +} + + +/** + * List DHCP servers. + * + * @returns See produceList. + * @param pVirtualBox Reference to the IVirtualBox smart pointer. + */ +static HRESULT listDhcpServers(const ComPtr &pVirtualBox) +{ + HRESULT hrcRet = S_OK; + com::SafeIfaceArray DHCPServers; + CHECK_ERROR2I_RET(pVirtualBox, COMGETTER(DHCPServers)(ComSafeArrayAsOutParam(DHCPServers)), hrcCheck); + for (size_t i = 0; i < DHCPServers.size(); ++i) + { + if (i > 0) + RTPrintf("\n"); + + ComPtr ptrDHCPServer = DHCPServers[i]; + Bstr bstr; + CHECK_ERROR2I_STMT(ptrDHCPServer, COMGETTER(NetworkName)(bstr.asOutParam()), hrcRet = hrcCheck); + RTPrintf(List::tr("NetworkName: %ls\n"), bstr.raw()); + + CHECK_ERROR2I_STMT(ptrDHCPServer, COMGETTER(IPAddress)(bstr.asOutParam()), hrcRet = hrcCheck); + RTPrintf("Dhcpd IP: %ls\n", bstr.raw()); + + CHECK_ERROR2I_STMT(ptrDHCPServer, COMGETTER(LowerIP)(bstr.asOutParam()), hrcRet = hrcCheck); + RTPrintf(List::tr("LowerIPAddress: %ls\n"), bstr.raw()); + + CHECK_ERROR2I_STMT(ptrDHCPServer, COMGETTER(UpperIP)(bstr.asOutParam()), hrcRet = hrcCheck); + RTPrintf(List::tr("UpperIPAddress: %ls\n"), bstr.raw()); + + CHECK_ERROR2I_STMT(ptrDHCPServer, COMGETTER(NetworkMask)(bstr.asOutParam()), hrcRet = hrcCheck); + RTPrintf(List::tr("NetworkMask: %ls\n"), bstr.raw()); + + BOOL fEnabled = FALSE; + CHECK_ERROR2I_STMT(ptrDHCPServer, COMGETTER(Enabled)(&fEnabled), hrcRet = hrcCheck); + RTPrintf(List::tr("Enabled: %s\n"), fEnabled ? List::tr("Yes") : List::tr("No")); + + /* Global configuration: */ + RTPrintf(List::tr("Global Configuration:\n")); + HRESULT hrc; + ComPtr ptrGlobal; + CHECK_ERROR2_STMT(hrc, ptrDHCPServer, COMGETTER(GlobalConfig)(ptrGlobal.asOutParam()), hrcRet = hrc); + if (SUCCEEDED(hrc)) + { + hrc = showDhcpConfig(ptrGlobal); + if (FAILED(hrc)) + hrcRet = hrc; + } + + /* Group configurations: */ + com::SafeIfaceArray Groups; + CHECK_ERROR2_STMT(hrc, ptrDHCPServer, COMGETTER(GroupConfigs)(ComSafeArrayAsOutParam(Groups)), hrcRet = hrc); + if (FAILED(hrc)) + RTPrintf(List::tr("Groups: %Rrc\n"), hrc); + else if (Groups.size() == 0) + RTPrintf(List::tr("Groups: None\n")); + else + { + for (size_t iGrp = 0; iGrp < Groups.size(); iGrp++) + { + CHECK_ERROR2I_STMT(Groups[iGrp], COMGETTER(Name)(bstr.asOutParam()), hrcRet = hrcCheck); + RTPrintf(List::tr("Group: %ls\n"), bstr.raw()); + + com::SafeIfaceArray Conditions; + CHECK_ERROR2_STMT(hrc, Groups[iGrp], COMGETTER(Conditions)(ComSafeArrayAsOutParam(Conditions)), hrcRet = hrc); + if (FAILED(hrc)) + RTPrintf(List::tr(" Conditions: %Rhrc\n"), hrc); + else if (Conditions.size() == 0) + RTPrintf(List::tr(" Conditions: None\n")); + else + for (size_t iCond = 0; iCond < Conditions.size(); iCond++) + { + BOOL fInclusive = TRUE; + CHECK_ERROR2_STMT(hrc, Conditions[iCond], COMGETTER(Inclusive)(&fInclusive), hrcRet = hrc); + DHCPGroupConditionType_T enmType = DHCPGroupConditionType_MAC; + CHECK_ERROR2_STMT(hrc, Conditions[iCond], COMGETTER(Type)(&enmType), hrcRet = hrc); + CHECK_ERROR2_STMT(hrc, Conditions[iCond], COMGETTER(Value)(bstr.asOutParam()), hrcRet = hrc); + + RTPrintf(List::tr(" Conditions: %s %s %ls\n"), + fInclusive ? List::tr("include") : List::tr("exclude"), + enmType == DHCPGroupConditionType_MAC ? "MAC " + : enmType == DHCPGroupConditionType_MACWildcard ? "MAC* " + : enmType == DHCPGroupConditionType_vendorClassID ? "VendorCID " + : enmType == DHCPGroupConditionType_vendorClassIDWildcard ? "VendorCID*" + : enmType == DHCPGroupConditionType_userClassID ? "UserCID " + : enmType == DHCPGroupConditionType_userClassIDWildcard ? "UserCID* " + : "!UNKNOWN! ", + bstr.raw()); + } + + hrc = showDhcpConfig(Groups[iGrp]); + if (FAILED(hrc)) + hrcRet = hrc; + } + Groups.setNull(); + } + + /* Individual host / NIC configurations: */ + com::SafeIfaceArray Hosts; + CHECK_ERROR2_STMT(hrc, ptrDHCPServer, COMGETTER(IndividualConfigs)(ComSafeArrayAsOutParam(Hosts)), hrcRet = hrc); + if (FAILED(hrc)) + RTPrintf(List::tr("Individual Configs: %Rrc\n"), hrc); + else if (Hosts.size() == 0) + RTPrintf(List::tr("Individual Configs: None\n")); + else + { + for (size_t iHost = 0; iHost < Hosts.size(); iHost++) + { + DHCPConfigScope_T enmScope = DHCPConfigScope_MAC; + CHECK_ERROR2I_STMT(Hosts[iHost], COMGETTER(Scope)(&enmScope), hrcRet = hrcCheck); + + if (enmScope == DHCPConfigScope_MAC) + { + CHECK_ERROR2I_STMT(Hosts[iHost], COMGETTER(MACAddress)(bstr.asOutParam()), hrcRet = hrcCheck); + RTPrintf(List::tr("Individual Config: MAC %ls\n"), bstr.raw()); + } + else + { + ULONG uSlot = 0; + CHECK_ERROR2I_STMT(Hosts[iHost], COMGETTER(Slot)(&uSlot), hrcRet = hrcCheck); + CHECK_ERROR2I_STMT(Hosts[iHost], COMGETTER(MachineId)(bstr.asOutParam()), hrcRet = hrcCheck); + Bstr bstrMACAddress; + hrc = Hosts[iHost]->COMGETTER(MACAddress)(bstrMACAddress.asOutParam()); /* No CHECK_ERROR2 stuff! */ + if (SUCCEEDED(hrc)) + RTPrintf(List::tr("Individual Config: VM NIC: %ls slot %u, MAC %ls\n"), bstr.raw(), uSlot, + bstrMACAddress.raw()); + else + RTPrintf(List::tr("Individual Config: VM NIC: %ls slot %u, MAC %Rhrc\n"), bstr.raw(), uSlot, hrc); + } + + CHECK_ERROR2I_STMT(Hosts[iHost], COMGETTER(FixedAddress)(bstr.asOutParam()), hrcRet = hrcCheck); + if (bstr.isNotEmpty()) + RTPrintf(List::tr(" Fixed Address: %ls\n"), bstr.raw()); + else + RTPrintf(List::tr(" Fixed Address: dynamic\n")); + + hrc = showDhcpConfig(Hosts[iHost]); + if (FAILED(hrc)) + hrcRet = hrc; + } + Hosts.setNull(); + } + } + + return hrcRet; +} + +/** + * List extension packs. + * + * @returns See produceList. + * @param pVirtualBox Reference to the IVirtualBox smart pointer. + */ +static HRESULT listExtensionPacks(const ComPtr &pVirtualBox) +{ + ComObjPtr ptrExtPackMgr; + CHECK_ERROR2I_RET(pVirtualBox, COMGETTER(ExtensionPackManager)(ptrExtPackMgr.asOutParam()), hrcCheck); + + SafeIfaceArray extPacks; + CHECK_ERROR2I_RET(ptrExtPackMgr, COMGETTER(InstalledExtPacks)(ComSafeArrayAsOutParam(extPacks)), hrcCheck); + RTPrintf(List::tr("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()); + Bstr bstrCryptoModule; + CHECK_ERROR2I_STMT(extPacks[i], COMGETTER(CryptoModule)(bstrCryptoModule.asOutParam()),hrc=hrcCheck; bstrCryptoModule.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(List::tr( + "Pack no.%2zu: %ls\n" + "Version: %ls\n" + "Revision: %u\n" + "Edition: %ls\n" + "Description: %ls\n" + "VRDE Module: %ls\n" + "Crypto Module: %ls\n" + "Usable: %RTbool\n" + "Why unusable: %ls\n"), + i, bstrName.raw(), + bstrVersion.raw(), + uRevision, + bstrEdition.raw(), + bstrDesc.raw(), + bstrVrdeModule.raw(), + bstrCryptoModule.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 &pVirtualBox) +{ + SafeArray 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 &pVirtualBox) +{ + HRESULT hrc; + ComPtr host; + CHECK_ERROR(pVirtualBox, COMGETTER(Host)(host.asOutParam())); + com::SafeIfaceArray hostVideoInputDevices; + CHECK_ERROR(host, COMGETTER(VideoInputDevices)(ComSafeArrayAsOutParam(hostVideoInputDevices))); + RTPrintf(List::tr("Video Input Devices: %u\n"), hostVideoInputDevices.size()); + for (size_t i = 0; i < hostVideoInputDevices.size(); ++i) + { + ComPtr 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 hrc; +} + +/** + * List supported screen shot formats. + * + * @returns See produceList. + * @param pVirtualBox Reference to the IVirtualBox pointer. + */ +static HRESULT listScreenShotFormats(const ComPtr &pVirtualBox) +{ + HRESULT hrc = S_OK; + ComPtr systemProperties; + CHECK_ERROR(pVirtualBox, COMGETTER(SystemProperties)(systemProperties.asOutParam())); + com::SafeArray formats; + CHECK_ERROR(systemProperties, COMGETTER(ScreenShotFormats)(ComSafeArrayAsOutParam(formats))); + + RTPrintf(List::tr("Supported %d screen shot formats:\n", "", formats.size()), 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 hrc; +} + +/** + * List available cloud providers. + * + * @returns See produceList. + * @param pVirtualBox Reference to the IVirtualBox pointer. + */ +static HRESULT listCloudProviders(const ComPtr &pVirtualBox) +{ + HRESULT hrc = S_OK; + ComPtr pCloudProviderManager; + CHECK_ERROR(pVirtualBox, COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam())); + com::SafeIfaceArray apCloudProviders; + CHECK_ERROR(pCloudProviderManager, COMGETTER(Providers)(ComSafeArrayAsOutParam(apCloudProviders))); + + RTPrintf(List::tr("Supported %d cloud providers:\n", "", apCloudProviders.size()), apCloudProviders.size()); + for (size_t i = 0; i < apCloudProviders.size(); ++i) + { + ComPtr pCloudProvider = apCloudProviders[i]; + Bstr bstrProviderName; + pCloudProvider->COMGETTER(Name)(bstrProviderName.asOutParam()); + RTPrintf(List::tr("Name: %ls\n"), bstrProviderName.raw()); + pCloudProvider->COMGETTER(ShortName)(bstrProviderName.asOutParam()); + RTPrintf(List::tr("Short Name: %ls\n"), bstrProviderName.raw()); + Bstr bstrProviderID; + pCloudProvider->COMGETTER(Id)(bstrProviderID.asOutParam()); + RTPrintf("GUID: %ls\n", bstrProviderID.raw()); + + RTPrintf("\n"); + } + return hrc; +} + + +/** + * 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 &pVirtualBox, bool fOptLong) +{ + HRESULT hrc = S_OK; + ComPtr pCloudProviderManager; + CHECK_ERROR(pVirtualBox, COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam())); + com::SafeIfaceArray apCloudProviders; + CHECK_ERROR(pCloudProviderManager, COMGETTER(Providers)(ComSafeArrayAsOutParam(apCloudProviders))); + + for (size_t i = 0; i < apCloudProviders.size(); ++i) + { + ComPtr pCloudProvider = apCloudProviders[i]; + com::SafeIfaceArray apCloudProfiles; + CHECK_ERROR(pCloudProvider, COMGETTER(Profiles)(ComSafeArrayAsOutParam(apCloudProfiles))); + for (size_t j = 0; j < apCloudProfiles.size(); ++j) + { + ComPtr pCloudProfile = apCloudProfiles[j]; + Bstr bstrProfileName; + pCloudProfile->COMGETTER(Name)(bstrProfileName.asOutParam()); + RTPrintf(List::tr("Name: %ls\n"), bstrProfileName.raw()); + Bstr bstrProviderID; + pCloudProfile->COMGETTER(ProviderId)(bstrProviderID.asOutParam()); + RTPrintf(List::tr("Provider GUID: %ls\n"), bstrProviderID.raw()); + + if (fOptLong) + { + com::SafeArray names; + com::SafeArray 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 ? List::tr("Property: ") : " ", + names[k], value.raw()); + fFirst = false; + } + } + + RTPrintf("\n"); + } + } + return hrc; +} + +static HRESULT displayCPUProfile(ICPUProfile *pProfile, size_t idx, int cchIdx, bool fOptLong, HRESULT hrc) +{ + /* Retrieve the attributes needed for both long and short display. */ + Bstr bstrName; + CHECK_ERROR2I_RET(pProfile, COMGETTER(Name)(bstrName.asOutParam()), hrcCheck); + + CPUArchitecture_T enmArchitecture = CPUArchitecture_Any; + CHECK_ERROR2I_RET(pProfile, COMGETTER(Architecture)(&enmArchitecture), hrcCheck); + const char *pszArchitecture = "???"; + switch (enmArchitecture) + { + case CPUArchitecture_x86: pszArchitecture = "x86"; break; + case CPUArchitecture_AMD64: pszArchitecture = "AMD64"; break; + +#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK + case CPUArchitecture_32BitHack: +#endif + case CPUArchitecture_Any: + break; + } + + /* Print what we've got. */ + if (!fOptLong) + RTPrintf("#%0*zu: %ls [%s]\n", cchIdx, idx, bstrName.raw(), pszArchitecture); + else + { + RTPrintf(List::tr("CPU Profile #%02zu:\n"), idx); + RTPrintf(List::tr(" Architecture: %s\n"), pszArchitecture); + RTPrintf(List::tr(" Name: %ls\n"), bstrName.raw()); + CHECK_ERROR2I_RET(pProfile, COMGETTER(FullName)(bstrName.asOutParam()), hrcCheck); + RTPrintf(List::tr(" Full Name: %ls\n"), bstrName.raw()); + } + return hrc; +} + + +/** + * List all CPU profiles. + * + * @returns See produceList. + * @param ptrVirtualBox Reference to the smart IVirtualBox pointer. + * @param fOptLong If true, list all profile properties. + * @param fOptSorted Sort the output if true, otherwise display in + * system order. + */ +static HRESULT listCPUProfiles(const ComPtr &ptrVirtualBox, bool fOptLong, bool fOptSorted) +{ + ComPtr ptrSysProps; + CHECK_ERROR2I_RET(ptrVirtualBox, COMGETTER(SystemProperties)(ptrSysProps.asOutParam()), hrcCheck); + com::SafeIfaceArray aCPUProfiles; + CHECK_ERROR2I_RET(ptrSysProps, GetCPUProfiles(CPUArchitecture_Any, Bstr().raw(), + ComSafeArrayAsOutParam(aCPUProfiles)), hrcCheck); + + int const cchIdx = 1 + (aCPUProfiles.size() >= 10) + (aCPUProfiles.size() >= 100); + + HRESULT hrc = S_OK; + if (!fOptSorted) + for (size_t i = 0; i < aCPUProfiles.size(); i++) + hrc = displayCPUProfile(aCPUProfiles[i], i, cchIdx, fOptLong, hrc); + else + { + std::vector > vecSortedProfiles; + for (size_t i = 0; i < aCPUProfiles.size(); ++i) + { + Bstr bstrName; + CHECK_ERROR2I_RET(aCPUProfiles[i], COMGETTER(Name)(bstrName.asOutParam()), hrcCheck); + try + { + vecSortedProfiles.push_back(std::pair(bstrName, aCPUProfiles[i])); + } + catch (std::bad_alloc &) + { + return E_OUTOFMEMORY; + } + } + + std::sort(vecSortedProfiles.begin(), vecSortedProfiles.end()); + + for (size_t i = 0; i < vecSortedProfiles.size(); i++) + hrc = displayCPUProfile(vecSortedProfiles[i].second, i, cchIdx, fOptLong, hrc); + } + + return hrc; +} + + +/** + * Translates PartitionType_T to a string if possible. + * @returns read-only string if known value, @a pszUnknown if not. + */ +static const char *PartitionTypeToString(PartitionType_T enmType, const char *pszUnknown) +{ +#define MY_CASE_STR(a_Type) case RT_CONCAT(PartitionType_,a_Type): return #a_Type + switch (enmType) + { + MY_CASE_STR(Empty); + MY_CASE_STR(FAT12); + MY_CASE_STR(FAT16); + MY_CASE_STR(FAT); + MY_CASE_STR(IFS); + MY_CASE_STR(FAT32CHS); + MY_CASE_STR(FAT32LBA); + MY_CASE_STR(FAT16B); + MY_CASE_STR(Extended); + MY_CASE_STR(WindowsRE); + MY_CASE_STR(LinuxSwapOld); + MY_CASE_STR(LinuxOld); + MY_CASE_STR(DragonFlyBSDSlice); + MY_CASE_STR(LinuxSwap); + MY_CASE_STR(Linux); + MY_CASE_STR(LinuxExtended); + MY_CASE_STR(LinuxLVM); + MY_CASE_STR(BSDSlice); + MY_CASE_STR(AppleUFS); + MY_CASE_STR(AppleHFS); + MY_CASE_STR(Solaris); + MY_CASE_STR(GPT); + MY_CASE_STR(EFI); + MY_CASE_STR(Unknown); + MY_CASE_STR(MBR); + MY_CASE_STR(iFFS); + MY_CASE_STR(SonyBoot); + MY_CASE_STR(LenovoBoot); + MY_CASE_STR(WindowsMSR); + MY_CASE_STR(WindowsBasicData); + MY_CASE_STR(WindowsLDMMeta); + MY_CASE_STR(WindowsLDMData); + MY_CASE_STR(WindowsRecovery); + MY_CASE_STR(WindowsStorageSpaces); + MY_CASE_STR(WindowsStorageReplica); + MY_CASE_STR(IBMGPFS); + MY_CASE_STR(LinuxData); + MY_CASE_STR(LinuxRAID); + MY_CASE_STR(LinuxRootX86); + MY_CASE_STR(LinuxRootAMD64); + MY_CASE_STR(LinuxRootARM32); + MY_CASE_STR(LinuxRootARM64); + MY_CASE_STR(LinuxHome); + MY_CASE_STR(LinuxSrv); + MY_CASE_STR(LinuxPlainDmCrypt); + MY_CASE_STR(LinuxLUKS); + MY_CASE_STR(LinuxReserved); + MY_CASE_STR(FreeBSDBoot); + MY_CASE_STR(FreeBSDData); + MY_CASE_STR(FreeBSDSwap); + MY_CASE_STR(FreeBSDUFS); + MY_CASE_STR(FreeBSDVinum); + MY_CASE_STR(FreeBSDZFS); + MY_CASE_STR(FreeBSDUnknown); + MY_CASE_STR(AppleHFSPlus); + MY_CASE_STR(AppleAPFS); + MY_CASE_STR(AppleRAID); + MY_CASE_STR(AppleRAIDOffline); + MY_CASE_STR(AppleBoot); + MY_CASE_STR(AppleLabel); + MY_CASE_STR(AppleTvRecovery); + MY_CASE_STR(AppleCoreStorage); + MY_CASE_STR(SoftRAIDStatus); + MY_CASE_STR(SoftRAIDScratch); + MY_CASE_STR(SoftRAIDVolume); + MY_CASE_STR(SoftRAIDCache); + MY_CASE_STR(AppleUnknown); + MY_CASE_STR(SolarisBoot); + MY_CASE_STR(SolarisRoot); + MY_CASE_STR(SolarisSwap); + MY_CASE_STR(SolarisBackup); + MY_CASE_STR(SolarisUsr); + MY_CASE_STR(SolarisVar); + MY_CASE_STR(SolarisHome); + MY_CASE_STR(SolarisAltSector); + MY_CASE_STR(SolarisReserved); + MY_CASE_STR(SolarisUnknown); + MY_CASE_STR(NetBSDSwap); + MY_CASE_STR(NetBSDFFS); + MY_CASE_STR(NetBSDLFS); + MY_CASE_STR(NetBSDRAID); + MY_CASE_STR(NetBSDConcatenated); + MY_CASE_STR(NetBSDEncrypted); + MY_CASE_STR(NetBSDUnknown); + MY_CASE_STR(ChromeOSKernel); + MY_CASE_STR(ChromeOSRootFS); + MY_CASE_STR(ChromeOSFuture); + MY_CASE_STR(ContLnxUsr); + MY_CASE_STR(ContLnxRoot); + MY_CASE_STR(ContLnxReserved); + MY_CASE_STR(ContLnxRootRAID); + MY_CASE_STR(HaikuBFS); + MY_CASE_STR(MidntBSDBoot); + MY_CASE_STR(MidntBSDData); + MY_CASE_STR(MidntBSDSwap); + MY_CASE_STR(MidntBSDUFS); + MY_CASE_STR(MidntBSDVium); + MY_CASE_STR(MidntBSDZFS); + MY_CASE_STR(MidntBSDUnknown); + MY_CASE_STR(OpenBSDData); + MY_CASE_STR(QNXPowerSafeFS); + MY_CASE_STR(Plan9); + MY_CASE_STR(VMWareVMKCore); + MY_CASE_STR(VMWareVMFS); + MY_CASE_STR(VMWareReserved); + MY_CASE_STR(VMWareUnknown); + MY_CASE_STR(AndroidX86Bootloader); + MY_CASE_STR(AndroidX86Bootloader2); + MY_CASE_STR(AndroidX86Boot); + MY_CASE_STR(AndroidX86Recovery); + MY_CASE_STR(AndroidX86Misc); + MY_CASE_STR(AndroidX86Metadata); + MY_CASE_STR(AndroidX86System); + MY_CASE_STR(AndroidX86Cache); + MY_CASE_STR(AndroidX86Data); + MY_CASE_STR(AndroidX86Persistent); + MY_CASE_STR(AndroidX86Vendor); + MY_CASE_STR(AndroidX86Config); + MY_CASE_STR(AndroidX86Factory); + MY_CASE_STR(AndroidX86FactoryAlt); + MY_CASE_STR(AndroidX86Fastboot); + MY_CASE_STR(AndroidX86OEM); + MY_CASE_STR(AndroidARMMeta); + MY_CASE_STR(AndroidARMExt); + MY_CASE_STR(ONIEBoot); + MY_CASE_STR(ONIEConfig); + MY_CASE_STR(PowerPCPrep); + MY_CASE_STR(XDGShrBootConfig); + MY_CASE_STR(CephBlock); + MY_CASE_STR(CephBlockDB); + MY_CASE_STR(CephBlockDBDmc); + MY_CASE_STR(CephBlockDBDmcLUKS); + MY_CASE_STR(CephBlockDmc); + MY_CASE_STR(CephBlockDmcLUKS); + MY_CASE_STR(CephBlockWALog); + MY_CASE_STR(CephBlockWALogDmc); + MY_CASE_STR(CephBlockWALogDmcLUKS); + MY_CASE_STR(CephDisk); + MY_CASE_STR(CephDiskDmc); + MY_CASE_STR(CephJournal); + MY_CASE_STR(CephJournalDmc); + MY_CASE_STR(CephJournalDmcLUKS); + MY_CASE_STR(CephLockbox); + MY_CASE_STR(CephMultipathBlock1); + MY_CASE_STR(CephMultipathBlock2); + MY_CASE_STR(CephMultipathBlockDB); + MY_CASE_STR(CephMultipathBLockWALog); + MY_CASE_STR(CephMultipathJournal); + MY_CASE_STR(CephMultipathOSD); + MY_CASE_STR(CephOSD); + MY_CASE_STR(CephOSDDmc); + MY_CASE_STR(CephOSDDmcLUKS); +#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK + case PartitionType_32BitHack: break; +#endif + /* no default! */ + } +#undef MY_CASE_STR + return pszUnknown; +} + + +/** + * List all available host drives with their partitions. + * + * @returns See produceList. + * @param pVirtualBox Reference to the IVirtualBox pointer. + * @param fOptLong Long listing or human readable. + */ +static HRESULT listHostDrives(const ComPtr pVirtualBox, bool fOptLong) +{ + HRESULT hrc = S_OK; + ComPtr pHost; + CHECK_ERROR2I_RET(pVirtualBox, COMGETTER(Host)(pHost.asOutParam()), hrcCheck); + com::SafeIfaceArray apHostDrives; + CHECK_ERROR2I_RET(pHost, COMGETTER(HostDrives)(ComSafeArrayAsOutParam(apHostDrives)), hrcCheck); + for (size_t i = 0; i < apHostDrives.size(); ++i) + { + ComPtr pHostDrive = apHostDrives[i]; + + /* The drivePath and model attributes are accessible even when the object + is in 'limited' mode. */ + com::Bstr bstrDrivePath; + CHECK_ERROR(pHostDrive,COMGETTER(DrivePath)(bstrDrivePath.asOutParam())); + if (SUCCEEDED(hrc)) + RTPrintf(List::tr("%sDrive: %ls\n"), i > 0 ? "\n" : "", bstrDrivePath.raw()); + else + RTPrintf(List::tr("%sDrive: %Rhrc\n"), i > 0 ? "\n" : "", hrc); + + com::Bstr bstrModel; + CHECK_ERROR(pHostDrive,COMGETTER(Model)(bstrModel.asOutParam())); + if (FAILED(hrc)) + RTPrintf(List::tr("Model: %Rhrc\n"), hrc); + else if (bstrModel.isNotEmpty()) + RTPrintf(List::tr("Model: \"%ls\"\n"), bstrModel.raw()); + else + RTPrintf(List::tr("Model: unknown/inaccessible\n")); + + /* The other attributes are not accessible in limited mode and will fail + with E_ACCESSDENIED. Typically means the user cannot read the drive. */ + com::Bstr bstrUuidDisk; + hrc = pHostDrive->COMGETTER(Uuid)(bstrUuidDisk.asOutParam()); + if (SUCCEEDED(hrc) && !com::Guid(bstrUuidDisk).isZero()) + RTPrintf("UUID: %ls\n", bstrUuidDisk.raw()); + else if (hrc == E_ACCESSDENIED) + { + RTPrintf(List::tr("Further disk and partitioning information is not available for drive \"%ls\". (E_ACCESSDENIED)\n"), + bstrDrivePath.raw()); + continue; + } + else if (FAILED(hrc)) + { + RTPrintf("UUID: %Rhrc\n", hrc); + com::GlueHandleComErrorNoCtx(pHostDrive, hrc); + } + + LONG64 cbSize = 0; + hrc = pHostDrive->COMGETTER(Size)(&cbSize); + if (SUCCEEDED(hrc) && fOptLong) + RTPrintf(List::tr("Size: %llu bytes (%Rhcb)\n", "", cbSize), cbSize, cbSize); + else if (SUCCEEDED(hrc)) + RTPrintf(List::tr("Size: %Rhcb\n"), cbSize); + else + { + RTPrintf(List::tr("Size: %Rhrc\n"), hrc); + com::GlueHandleComErrorNoCtx(pHostDrive, hrc); + } + + ULONG cbSectorSize = 0; + hrc = pHostDrive->COMGETTER(SectorSize)(&cbSectorSize); + if (SUCCEEDED(hrc)) + RTPrintf(List::tr("Sector Size: %u bytes\n", "", cbSectorSize), cbSectorSize); + else + { + RTPrintf(List::tr("Sector Size: %Rhrc\n"), hrc); + com::GlueHandleComErrorNoCtx(pHostDrive, hrc); + } + + PartitioningType_T partitioningType = (PartitioningType_T)9999; + hrc = pHostDrive->COMGETTER(PartitioningType)(&partitioningType); + if (SUCCEEDED(hrc)) + RTPrintf(List::tr("Scheme: %s\n"), partitioningType == PartitioningType_MBR ? "MBR" : "GPT"); + else + { + RTPrintf(List::tr("Scheme: %Rhrc\n"), hrc); + com::GlueHandleComErrorNoCtx(pHostDrive, hrc); + } + + com::SafeIfaceArray apHostDrivesPartitions; + hrc = pHostDrive->COMGETTER(Partitions)(ComSafeArrayAsOutParam(apHostDrivesPartitions)); + if (FAILED(hrc)) + { + RTPrintf(List::tr("Partitions: %Rhrc\n"), hrc); + com::GlueHandleComErrorNoCtx(pHostDrive, hrc); + } + else if (apHostDrivesPartitions.size() == 0) + RTPrintf(List::tr("Partitions: None (or not able to grok them).\n")); + else if (partitioningType == PartitioningType_MBR) + { + if (fOptLong) + RTPrintf(List::tr("Partitions: First Last\n" + "## Type Byte Size Byte Offset Cyl/Head/Sec Cyl/Head/Sec Active\n")); + else + RTPrintf(List::tr("Partitions: First Last\n" + "## Type Size Start Cyl/Head/Sec Cyl/Head/Sec Active\n")); + for (size_t j = 0; j < apHostDrivesPartitions.size(); ++j) + { + ComPtr pHostDrivePartition = apHostDrivesPartitions[j]; + + ULONG idx = 0; + CHECK_ERROR(pHostDrivePartition, COMGETTER(Number)(&idx)); + ULONG uType = 0; + CHECK_ERROR(pHostDrivePartition, COMGETTER(TypeMBR)(&uType)); + ULONG uStartCylinder = 0; + CHECK_ERROR(pHostDrivePartition, COMGETTER(StartCylinder)(&uStartCylinder)); + ULONG uStartHead = 0; + CHECK_ERROR(pHostDrivePartition, COMGETTER(StartHead)(&uStartHead)); + ULONG uStartSector = 0; + CHECK_ERROR(pHostDrivePartition, COMGETTER(StartSector)(&uStartSector)); + ULONG uEndCylinder = 0; + CHECK_ERROR(pHostDrivePartition, COMGETTER(EndCylinder)(&uEndCylinder)); + ULONG uEndHead = 0; + CHECK_ERROR(pHostDrivePartition, COMGETTER(EndHead)(&uEndHead)); + ULONG uEndSector = 0; + CHECK_ERROR(pHostDrivePartition, COMGETTER(EndSector)(&uEndSector)); + cbSize = 0; + CHECK_ERROR(pHostDrivePartition, COMGETTER(Size)(&cbSize)); + LONG64 offStart = 0; + CHECK_ERROR(pHostDrivePartition, COMGETTER(Start)(&offStart)); + BOOL fActive = 0; + CHECK_ERROR(pHostDrivePartition, COMGETTER(Active)(&fActive)); + PartitionType_T enmType = PartitionType_Unknown; + CHECK_ERROR(pHostDrivePartition, COMGETTER(Type)(&enmType)); + + /* Max size & offset here is around 16TiB with 4KiB sectors. */ + if (fOptLong) /* cb/off: max 16TiB; idx: max 64. */ + RTPrintf("%2u %02x %14llu %14llu %4u/%3u/%2u %4u/%3u/%2u %s %s\n", + idx, uType, cbSize, offStart, + uStartCylinder, uStartHead, uStartSector, uEndCylinder, uEndHead, uEndSector, + fActive ? List::tr("yes") : List::tr("no"), PartitionTypeToString(enmType, "")); + else + RTPrintf("%2u %02x %8Rhcb %8Rhcb %4u/%3u/%2u %4u/%3u/%2u %s %s\n", + idx, uType, (uint64_t)cbSize, (uint64_t)offStart, + uStartCylinder, uStartHead, uStartSector, uEndCylinder, uEndHead, uEndSector, + fActive ? List::tr("yes") : List::tr("no"), PartitionTypeToString(enmType, "")); + } + } + else /* GPT */ + { + /* Determin the max partition type length to try reduce the table width: */ + size_t cchMaxType = 0; + for (size_t j = 0; j < apHostDrivesPartitions.size(); ++j) + { + ComPtr pHostDrivePartition = apHostDrivesPartitions[j]; + PartitionType_T enmType = PartitionType_Unknown; + CHECK_ERROR(pHostDrivePartition, COMGETTER(Type)(&enmType)); + size_t const cchTypeNm = strlen(PartitionTypeToString(enmType, "e530bf6d-2754-4e9d-b260-60a5d0b80457")); + cchMaxType = RT_MAX(cchTypeNm, cchMaxType); + } + cchMaxType = RT_MIN(cchMaxType, RTUUID_STR_LENGTH); + + if (fOptLong) + RTPrintf(List::tr( + "Partitions:\n" + "## %-*s Uuid Byte Size Byte Offset Active Name\n"), + (int)cchMaxType, List::tr("Type")); + else + RTPrintf(List::tr( + "Partitions:\n" + "## %-*s Uuid Size Start Active Name\n"), + (int)cchMaxType, List::tr("Type")); + + for (size_t j = 0; j < apHostDrivesPartitions.size(); ++j) + { + ComPtr pHostDrivePartition = apHostDrivesPartitions[j]; + + ULONG idx = 0; + CHECK_ERROR(pHostDrivePartition, COMGETTER(Number)(&idx)); + com::Bstr bstrUuidType; + CHECK_ERROR(pHostDrivePartition, COMGETTER(TypeUuid)(bstrUuidType.asOutParam())); + com::Bstr bstrUuidPartition; + CHECK_ERROR(pHostDrivePartition, COMGETTER(Uuid)(bstrUuidPartition.asOutParam())); + cbSize = 0; + CHECK_ERROR(pHostDrivePartition, COMGETTER(Size)(&cbSize)); + LONG64 offStart = 0; + CHECK_ERROR(pHostDrivePartition, COMGETTER(Start)(&offStart)); + BOOL fActive = 0; + CHECK_ERROR(pHostDrivePartition, COMGETTER(Active)(&fActive)); + com::Bstr bstrName; + CHECK_ERROR(pHostDrivePartition, COMGETTER(Name)(bstrName.asOutParam())); + + PartitionType_T enmType = PartitionType_Unknown; + CHECK_ERROR(pHostDrivePartition, COMGETTER(Type)(&enmType)); + + Utf8Str strTypeConv; + const char *pszTypeNm = PartitionTypeToString(enmType, NULL); + if (!pszTypeNm) + pszTypeNm = (strTypeConv = bstrUuidType).c_str(); + else if (strlen(pszTypeNm) >= RTUUID_STR_LENGTH /* includes '\0' */) + pszTypeNm -= RTUUID_STR_LENGTH - 1 - strlen(pszTypeNm); + + if (fOptLong) + RTPrintf("%2u %-*s %36ls %19llu %19llu %-3s %ls\n", idx, cchMaxType, pszTypeNm, + bstrUuidPartition.raw(), cbSize, offStart, fActive ? List::tr("on") : List::tr("off"), + bstrName.raw()); + else + RTPrintf("%2u %-*s %36ls %8Rhcb %8Rhcb %-3s %ls\n", idx, cchMaxType, pszTypeNm, + bstrUuidPartition.raw(), cbSize, offStart, fActive ? List::tr("on") : List::tr("off"), + bstrName.raw()); + } + } + } + return hrc; +} + + +/** + * The type of lists we can produce. + */ +enum ListType_T +{ + kListNotSpecified = 1000, + kListVMs, + kListRunningVMs, + kListOsTypes, + kListHostDvds, + kListHostFloppies, + kListInternalNetworks, + kListBridgedInterfaces, +#if defined(VBOX_WITH_NETFLT) + kListHostOnlyInterfaces, +#endif +#if defined(VBOX_WITH_VMNET) + kListHostOnlyNetworks, +#endif +#if defined(VBOX_WITH_CLOUD_NET) + kListCloudNetworks, +#endif + kListHostCpuIDs, + kListHostInfo, + kListHddBackends, + kListHdds, + kListDvds, + kListFloppies, + kListUsbHost, + kListUsbFilters, + kListSystemProperties, +#if defined(VBOX_WITH_UPDATE_AGENT) + kListUpdateAgents, +#endif + kListDhcpServers, + kListExtPacks, + kListGroups, + kListNatNetworks, + kListVideoInputDevices, + kListScreenShotFormats, + kListCloudProviders, + kListCloudProfiles, + kListCPUProfiles, + kListHostDrives +}; + + +/** + * 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 ListType_T enmCommand, bool fOptLong, bool fOptSorted, const ComPtr &pVirtualBox) +{ + HRESULT hrc = S_OK; + switch (enmCommand) + { + case kListNotSpecified: + AssertFailed(); + return E_FAIL; + + case kListVMs: + { + /* + * Get the list of all registered VMs + */ + com::SafeIfaceArray machines; + hrc = pVirtualBox->COMGETTER(Machines)(ComSafeArrayAsOutParam(machines)); + if (SUCCEEDED(hrc)) + { + /* + * Display it. + */ + if (!fOptSorted) + { + for (size_t i = 0; i < machines.size(); ++i) + if (machines[i]) + hrc = showVMInfo(pVirtualBox, machines[i], NULL, fOptLong ? VMINFO_STANDARD : VMINFO_COMPACT); + } + else + { + /* + * Sort the list by name before displaying it. + */ + std::vector > 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(bstrName, pMachine)); + } + } + + std::sort(sortedMachines.begin(), sortedMachines.end()); + + for (size_t i = 0; i < sortedMachines.size(); ++i) + hrc = showVMInfo(pVirtualBox, sortedMachines[i].second, NULL, fOptLong ? VMINFO_STANDARD : VMINFO_COMPACT); + } + } + break; + } + + case kListRunningVMs: + { + /* + * Get the list of all _running_ VMs + */ + com::SafeIfaceArray machines; + hrc = pVirtualBox->COMGETTER(Machines)(ComSafeArrayAsOutParam(machines)); + com::SafeArray states; + if (SUCCEEDED(hrc)) + hrc = pVirtualBox->GetMachineStates(ComSafeArrayAsInParam(machines), ComSafeArrayAsOutParam(states)); + if (SUCCEEDED(hrc)) + { + /* + * 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: + hrc = showVMInfo(pVirtualBox, machines[i], NULL, fOptLong ? VMINFO_STANDARD : VMINFO_COMPACT); + break; + default: break; /* Shut up MSC */ + } + } + } + } + break; + } + + case kListOsTypes: + { + com::SafeIfaceArray coll; + hrc = pVirtualBox->COMGETTER(GuestOSTypes)(ComSafeArrayAsOutParam(coll)); + if (SUCCEEDED(hrc)) + { + /* + * Iterate through the collection. + */ + for (size_t i = 0; i < coll.size(); ++i) + { + ComPtr 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(List::tr("Description: %ls\n"), guestDescription.raw()); + Bstr familyId; + guestOS->COMGETTER(FamilyId)(familyId.asOutParam()); + RTPrintf(List::tr("Family ID: %ls\n"), familyId.raw()); + Bstr familyDescription; + guestOS->COMGETTER(FamilyDescription)(familyDescription.asOutParam()); + RTPrintf(List::tr("Family Desc: %ls\n"), familyDescription.raw()); + BOOL is64Bit; + guestOS->COMGETTER(Is64Bit)(&is64Bit); + RTPrintf(List::tr("64 bit: %RTbool\n"), is64Bit); + RTPrintf("\n"); + } + } + break; + } + + case kListHostDvds: + { + ComPtr host; + CHECK_ERROR(pVirtualBox, COMGETTER(Host)(host.asOutParam())); + com::SafeIfaceArray coll; + CHECK_ERROR(host, COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(coll))); + if (SUCCEEDED(hrc)) + { + for (size_t i = 0; i < coll.size(); ++i) + { + ComPtr 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(List::tr("Name: %ls\n\n"), location.raw()); + } + } + break; + } + + case kListHostFloppies: + { + ComPtr host; + CHECK_ERROR(pVirtualBox, COMGETTER(Host)(host.asOutParam())); + com::SafeIfaceArray coll; + CHECK_ERROR(host, COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(coll))); + if (SUCCEEDED(hrc)) + { + for (size_t i = 0; i < coll.size(); ++i) + { + ComPtr 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(List::tr("Name: %ls\n\n"), location.raw()); + } + } + break; + } + + case kListInternalNetworks: + hrc = listInternalNetworks(pVirtualBox); + break; + + case kListBridgedInterfaces: +#if defined(VBOX_WITH_NETFLT) + case kListHostOnlyInterfaces: +#endif + hrc = listNetworkInterfaces(pVirtualBox, enmCommand == kListBridgedInterfaces); + break; + +#if defined(VBOX_WITH_VMNET) + case kListHostOnlyNetworks: + hrc = listHostOnlyNetworks(pVirtualBox); + break; +#endif + +#if defined(VBOX_WITH_CLOUD_NET) + case kListCloudNetworks: + hrc = listCloudNetworks(pVirtualBox); + break; +#endif + case kListHostInfo: + hrc = listHostInfo(pVirtualBox); + break; + + case kListHostCpuIDs: + { + ComPtr Host; + CHECK_ERROR(pVirtualBox, COMGETTER(Host)(Host.asOutParam())); + + RTPrintf(List::tr("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: + hrc = listHddBackends(pVirtualBox); + break; + + case kListHdds: + { + com::SafeIfaceArray hdds; + CHECK_ERROR(pVirtualBox, COMGETTER(HardDisks)(ComSafeArrayAsOutParam(hdds))); + hrc = listMedia(pVirtualBox, hdds, List::tr("base"), fOptLong); + break; + } + + case kListDvds: + { + com::SafeIfaceArray dvds; + CHECK_ERROR(pVirtualBox, COMGETTER(DVDImages)(ComSafeArrayAsOutParam(dvds))); + hrc = listMedia(pVirtualBox, dvds, NULL, fOptLong); + break; + } + + case kListFloppies: + { + com::SafeIfaceArray floppies; + CHECK_ERROR(pVirtualBox, COMGETTER(FloppyImages)(ComSafeArrayAsOutParam(floppies))); + hrc = listMedia(pVirtualBox, floppies, NULL, fOptLong); + break; + } + + case kListUsbHost: + hrc = listUsbHost(pVirtualBox); + break; + + case kListUsbFilters: + hrc = listUsbFilters(pVirtualBox); + break; + + case kListSystemProperties: + hrc = listSystemProperties(pVirtualBox); + break; + +#ifdef VBOX_WITH_UPDATE_AGENT + case kListUpdateAgents: + hrc = listUpdateAgents(pVirtualBox); + break; +#endif + case kListDhcpServers: + hrc = listDhcpServers(pVirtualBox); + break; + + case kListExtPacks: + hrc = listExtensionPacks(pVirtualBox); + break; + + case kListGroups: + hrc = listGroups(pVirtualBox); + break; + + case kListNatNetworks: + hrc = listNATNetworks(fOptLong, fOptSorted, pVirtualBox); + break; + + case kListVideoInputDevices: + hrc = listVideoInputDevices(pVirtualBox); + break; + + case kListScreenShotFormats: + hrc = listScreenShotFormats(pVirtualBox); + break; + + case kListCloudProviders: + hrc = listCloudProviders(pVirtualBox); + break; + + case kListCloudProfiles: + hrc = listCloudProfiles(pVirtualBox, fOptLong); + break; + + case kListCPUProfiles: + hrc = listCPUProfiles(pVirtualBox, fOptLong, fOptSorted); + break; + + case kListHostDrives: + hrc = listHostDrives(pVirtualBox, fOptLong); + break; + /* No default here, want gcc warnings. */ + + } /* end switch */ + + return hrc; +} + +/** + * 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; + bool fFirst = true; + enum ListType_T enmOptCommand = kListNotSpecified; + RTEXITCODE rcExit = RTEXITCODE_SUCCESS; + + 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 +#if defined(VBOX_WITH_VMNET) + { "hostonlynets", kListHostOnlyNetworks, RTGETOPT_REQ_NOTHING }, +#endif +#if defined(VBOX_WITH_CLOUD_NET) + { "cloudnets", kListCloudNetworks, 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 }, +#if defined(VBOX_WITH_UPDATE_AGENT) + { "updates", kListUpdateAgents, RTGETOPT_REQ_NOTHING }, +#endif + { "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 }, + { "cpu-profiles", kListCPUProfiles, RTGETOPT_REQ_NOTHING }, + { "hostdrives", kListHostDrives, 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 +#if defined(VBOX_WITH_VMNET) + case kListHostOnlyNetworks: +#endif +#if defined(VBOX_WITH_CLOUD_NET) + case kListCloudNetworks: +#endif + case kListHostInfo: + case kListHostCpuIDs: + case kListHddBackends: + case kListHdds: + case kListDvds: + case kListFloppies: + case kListUsbHost: + case kListUsbFilters: + case kListSystemProperties: +#if defined(VBOX_WITH_UPDATE_AGENT) + case kListUpdateAgents: +#endif + case kListDhcpServers: + case kListExtPacks: + case kListGroups: + case kListNatNetworks: + case kListVideoInputDevices: + case kListScreenShotFormats: + case kListCloudProviders: + case kListCloudProfiles: + case kListCPUProfiles: + case kListHostDrives: + enmOptCommand = (enum ListType_T)ch; + if (fOptMultiple) + { + if (fFirst) + fFirst = false; + else + RTPrintf("\n"); + RTPrintf("[%s]\n", ValueUnion.pDef->pszLong); + HRESULT hrc = produceList(enmOptCommand, fOptLong, fOptSorted, a->virtualBox); + if (FAILED(hrc)) + rcExit = RTEXITCODE_FAILURE; + } + break; + + case VINF_GETOPT_NOT_OPTION: + return errorSyntax(List::tr("Unknown subcommand \"%s\"."), ValueUnion.psz); + + default: + return errorGetOpt(ch, &ValueUnion); + } + } + + /* + * If not in multiple list mode, we have to produce the list now. + */ + if (enmOptCommand == kListNotSpecified) + return errorSyntax(List::tr("Missing subcommand for \"list\" command.\n")); + if (!fOptMultiple) + { + HRESULT hrc = produceList(enmOptCommand, fOptLong, fOptSorted, a->virtualBox); + if (FAILED(hrc)) + rcExit = RTEXITCODE_FAILURE; + } + + return rcExit; +} + +/* 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..57ca8b34 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageMetrics.cpp @@ -0,0 +1,671 @@ +/* $Id: VBoxManageMetrics.cpp $ */ +/** @file + * VBoxManage - The 'metrics' command. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "VBoxManage.h" +using namespace com; + +DECLARE_TRANSLATION_CONTEXT(Metrics); + +// funcs +/////////////////////////////////////////////////////////////////////////////// + + +static HRESULT parseFilterParameters(int argc, char *argv[], + ComPtr aVirtualBox, + ComSafeArrayOut(BSTR, outMetrics), + ComSafeArrayOut(IUnknown *, outObjects)) +{ + HRESULT hrc = S_OK; + com::SafeArray retMetrics(1); + com::SafeIfaceArray 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 host; + CHECK_ERROR(aVirtualBox, COMGETTER(Host)(host.asOutParam())); + retObjects.reset(1); + host.queryInterfaceTo(&retObjects[0]); + } + else + { + ComPtr machine; + hrc = aVirtualBox->FindMachine(Bstr(argv[0]).raw(), + machine.asOutParam()); + if (SUCCEEDED(hrc)) + { + retObjects.reset(1); + machine.queryInterfaceTo(&retObjects[0]); + } + else + { + errorArgument(Metrics::tr("Invalid machine name: '%s'"), argv[0]); + return hrc; + } + } + + } + + retMetrics.detachTo(ComSafeArrayOutArg(outMetrics)); + retObjects.detachTo(ComSafeArrayOutArg(outObjects)); + + return hrc; +} + +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//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 aObject) +{ + HRESULT hrc; + + ComPtr host = aObject; + if (!host.isNull()) + return Bstr(Metrics::tr("host")); + + ComPtr machine = aObject; + if (!machine.isNull()) + { + Bstr name; + CHECK_ERROR(machine, COMGETTER(Name)(name.asOutParam())); + if (SUCCEEDED(hrc)) + return name; + } + return Bstr(Metrics::tr("unknown")); +} + +static void listAffectedMetrics(ComSafeArrayIn(IPerformanceMetric*, aMetrics)) +{ + HRESULT hrc; + com::SafeIfaceArray metrics(ComSafeArrayInArg(aMetrics)); + if (metrics.size()) + { + ComPtr object; + Bstr metricName; + RTPrintf(Metrics::tr("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(Metrics::tr("No metrics match the specified filter!")); + } +} + +/** + * list + */ +static RTEXITCODE handleMetricsList(int argc, char *argv[], + ComPtr aVirtualBox, + ComPtr performanceCollector) +{ + HRESULT hrc; + com::SafeArray metrics; + com::SafeIfaceArray objects; + + setCurrentSubcommand(HELP_SCOPE_METRICS_LIST); + + hrc = parseFilterParameters(argc - 1, &argv[1], aVirtualBox, + ComSafeArrayAsOutParam(metrics), + ComSafeArrayAsOutParam(objects)); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + com::SafeIfaceArray metricInfo; + + CHECK_ERROR(performanceCollector, + GetMetrics(ComSafeArrayAsInParam(metrics), + ComSafeArrayAsInParam(objects), + ComSafeArrayAsOutParam(metricInfo))); + + ComPtr object; + Bstr metricName, unit, description; + ULONG period, count; + LONG minimum, maximum; + RTPrintf(Metrics::tr( +"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 aVirtualBox, + ComPtr performanceCollector) +{ + HRESULT hrc; + com::SafeArray metrics; + com::SafeIfaceArray objects; + uint32_t period = 1, samples = 1; + bool listMatches = false; + int i; + + setCurrentSubcommand(HELP_SCOPE_METRICS_SETUP); + + for (i = 1; i < argc; i++) + { + if ( !strcmp(argv[i], "--period") + || !strcmp(argv[i], "-period")) + { + if (argc <= i + 1) + return errorArgument(Metrics::tr("Missing argument to '%s'"), argv[i]); + if ( VINF_SUCCESS != RTStrToUInt32Full(argv[++i], 10, &period) + || !period) + return errorArgument(Metrics::tr("Invalid value for 'period' parameter: '%s'"), argv[i]); + } + else if ( !strcmp(argv[i], "--samples") + || !strcmp(argv[i], "-samples")) + { + if (argc <= i + 1) + return errorArgument(Metrics::tr("Missing argument to '%s'"), argv[i]); + if ( VINF_SUCCESS != RTStrToUInt32Full(argv[++i], 10, &samples) + || !samples) + return errorArgument(Metrics::tr("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 */ + } + + hrc = parseFilterParameters(argc - i, &argv[i], aVirtualBox, + ComSafeArrayAsOutParam(metrics), + ComSafeArrayAsOutParam(objects)); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + com::SafeIfaceArray affectedMetrics; + CHECK_ERROR(performanceCollector, + SetupMetrics(ComSafeArrayAsInParam(metrics), + ComSafeArrayAsInParam(objects), period, samples, + ComSafeArrayAsOutParam(affectedMetrics))); + if (FAILED(hrc)) + 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 aVirtualBox, + ComPtr performanceCollector) +{ + HRESULT hrc; + com::SafeArray metrics; + com::SafeIfaceArray objects; + + setCurrentSubcommand(HELP_SCOPE_METRICS_QUERY); + + hrc = parseFilterParameters(argc - 1, &argv[1], aVirtualBox, + ComSafeArrayAsOutParam(metrics), + ComSafeArrayAsOutParam(objects)); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + com::SafeArray retNames; + com::SafeIfaceArray retObjects; + com::SafeArray retUnits; + com::SafeArray retScales; + com::SafeArray retSequenceNumbers; + com::SafeArray retIndices; + com::SafeArray retLengths; + com::SafeArray 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(Metrics::tr( + "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) RT_NOTHROW_DEF +{ + 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 aVirtualBox, + ComPtr performanceCollector) +{ + HRESULT hrc; + com::SafeArray metrics; + com::SafeIfaceArray objects; + uint32_t period = 1, samples = 1; + bool isDetached = false, listMatches = false; + int i; + + setCurrentSubcommand(HELP_SCOPE_METRICS_COLLECT); + + for (i = 1; i < argc; i++) + { + if ( !strcmp(argv[i], "--period") + || !strcmp(argv[i], "-period")) + { + if (argc <= i + 1) + return errorArgument(Metrics::tr("Missing argument to '%s'"), argv[i]); + if ( VINF_SUCCESS != RTStrToUInt32Full(argv[++i], 10, &period) + || !period) + return errorArgument(Metrics::tr("Invalid value for 'period' parameter: '%s'"), argv[i]); + } + else if ( !strcmp(argv[i], "--samples") + || !strcmp(argv[i], "-samples")) + { + if (argc <= i + 1) + return errorArgument(Metrics::tr("Missing argument to '%s'"), argv[i]); + if ( VINF_SUCCESS != RTStrToUInt32Full(argv[++i], 10, &samples) + || !samples) + return errorArgument(Metrics::tr("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 */ + } + + hrc = parseFilterParameters(argc - i, &argv[i], aVirtualBox, + ComSafeArrayAsOutParam(metrics), + ComSafeArrayAsOutParam(objects)); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + com::SafeIfaceArray metricInfo; + + CHECK_ERROR(performanceCollector, + GetMetrics(ComSafeArrayAsInParam(metrics), + ComSafeArrayAsInParam(objects), + ComSafeArrayAsOutParam(metricInfo))); + + std::set,Bstr> > baseMetrics; + ComPtr 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 baseMetricsFiltered(baseMetrics.size()); + com::SafeIfaceArray objectsFiltered(baseMetrics.size()); + std::set,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 affectedMetrics; + CHECK_ERROR(performanceCollector, + SetupMetrics(ComSafeArrayAsInParam(baseMetricsFiltered), + ComSafeArrayAsInParam(objectsFiltered), period, samples, + ComSafeArrayAsOutParam(affectedMetrics))); + if (FAILED(hrc)) + 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(Metrics::tr("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(Metrics::tr("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 retNames; + com::SafeIfaceArray retObjects; + com::SafeArray retUnits; + com::SafeArray retScales; + com::SafeArray retSequenceNumbers; + com::SafeArray retIndices; + com::SafeArray retLengths; + com::SafeArray 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 aVirtualBox, + ComPtr performanceCollector) +{ + HRESULT hrc; + com::SafeArray metrics; + com::SafeIfaceArray objects; + bool listMatches = false; + int i; + + setCurrentSubcommand(HELP_SCOPE_METRICS_ENABLE); + + 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 */ + } + + hrc = parseFilterParameters(argc - i, &argv[i], aVirtualBox, + ComSafeArrayAsOutParam(metrics), + ComSafeArrayAsOutParam(objects)); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + com::SafeIfaceArray affectedMetrics; + CHECK_ERROR(performanceCollector, + EnableMetrics(ComSafeArrayAsInParam(metrics), + ComSafeArrayAsInParam(objects), + ComSafeArrayAsOutParam(affectedMetrics))); + if (FAILED(hrc)) + 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 aVirtualBox, + ComPtr performanceCollector) +{ + HRESULT hrc; + com::SafeArray metrics; + com::SafeIfaceArray objects; + bool listMatches = false; + int i; + + setCurrentSubcommand(HELP_SCOPE_METRICS_DISABLE); + + 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 */ + } + + hrc = parseFilterParameters(argc - i, &argv[i], aVirtualBox, + ComSafeArrayAsOutParam(metrics), + ComSafeArrayAsOutParam(objects)); + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + com::SafeIfaceArray affectedMetrics; + CHECK_ERROR(performanceCollector, + DisableMetrics(ComSafeArrayAsInParam(metrics), + ComSafeArrayAsInParam(objects), + ComSafeArrayAsOutParam(affectedMetrics))); + if (FAILED(hrc)) + 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(Metrics::tr("Subcommand missing")); + + ComPtr 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(Metrics::tr("Invalid subcommand '%s'"), a->argv[0]); + + return rcExit; +} diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageMisc.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageMisc.cpp new file mode 100644 index 00000000..fca7a98c --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageMisc.cpp @@ -0,0 +1,2920 @@ +/* $Id: VBoxManageMisc.cpp $ */ +/** @file + * VBoxManage - VirtualBox's command-line interface. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "VBoxManage.h" + +#include + +using namespace com; + +DECLARE_TRANSLATION_CONTEXT(Misc); + +static const RTGETOPTDEF g_aRegisterVMOptions[] = +{ + { "--password", 'p', RTGETOPT_REQ_STRING }, +}; + +RTEXITCODE handleRegisterVM(HandlerArg *a) +{ + HRESULT hrc; + const char *VMName = NULL; + + Bstr bstrVMName; + Bstr bstrPasswordFile; + + 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_aRegisterVMOptions, RT_ELEMENTS(g_aRegisterVMOptions), + 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS); + while ((c = RTGetOpt(&GetState, &ValueUnion))) + { + switch (c) + { + case 'p': // --password + bstrPasswordFile = ValueUnion.psz; + break; + + case VINF_GETOPT_NOT_OPTION: + if (bstrVMName.isEmpty()) + VMName = ValueUnion.psz; + else + return errorSyntax(Misc::tr("Invalid parameter '%s'"), ValueUnion.psz); + break; + + default: + if (c > 0) + { + if (RT_C_IS_PRINT(c)) + return errorSyntax(Misc::tr("Invalid option -%c"), c); + return errorSyntax(Misc::tr("Invalid option case %i"), c); + } + if (c == VERR_GETOPT_UNKNOWN_OPTION) + return errorSyntax(Misc::tr("unknown option: %s\n"), ValueUnion.psz); + if (ValueUnion.pDef) + return errorSyntax("%s: %Rrs", ValueUnion.pDef->pszLong, c); + return errorSyntax(Misc::tr("error: %Rrs"), c); + } + } + + Utf8Str strPassword; + + if (bstrPasswordFile.isNotEmpty()) + { + if (bstrPasswordFile == "-") + { + /* Get password from console. */ + RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, Misc::tr("Enter password:")); + if (rcExit == RTEXITCODE_FAILURE) + return rcExit; + } + else + { + RTEXITCODE rcExit = readPasswordFile(a->argv[3], &strPassword); + if (rcExit == RTEXITCODE_FAILURE) + return RTMsgErrorExitFailure(Misc::tr("Failed to read password from file")); + } + } + + ComPtr 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. */ + hrc = a->virtualBox->OpenMachine(Bstr(a->argv[0]).raw(), + Bstr(strPassword).raw(), + machine.asOutParam()); + if (FAILED(hrc) && !RTPathStartsWithRoot(a->argv[0])) + { + char szVMFileAbs[RTPATH_MAX] = ""; + int vrc = RTPathAbs(a->argv[0], szVMFileAbs, sizeof(szVMFileAbs)); + if (RT_FAILURE(vrc)) + return RTMsgErrorExitFailure(Misc::tr("Failed to convert \"%s\" to an absolute path: %Rrc"), + a->argv[0], vrc); + CHECK_ERROR(a->virtualBox, OpenMachine(Bstr(szVMFileAbs).raw(), + Bstr(strPassword).raw(), + machine.asOutParam())); + } + else if (FAILED(hrc)) + com::GlueHandleComError(a->virtualBox, + "OpenMachine(Bstr(a->argv[0]).raw(), Bstr(strPassword).raw(), machine.asOutParam()))", + hrc, __FILE__, __LINE__); + if (SUCCEEDED(hrc)) + { + ASSERT(machine); + CHECK_ERROR(a->virtualBox, RegisterMachine(machine)); + } + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +static const RTGETOPTDEF g_aUnregisterVMOptions[] = +{ + { "--delete", 'd', RTGETOPT_REQ_NOTHING }, + { "-delete", 'd', RTGETOPT_REQ_NOTHING }, // deprecated + { "--delete-all", 'a', RTGETOPT_REQ_NOTHING }, + { "-delete-all", 'a', RTGETOPT_REQ_NOTHING }, // deprecated +}; + +RTEXITCODE handleUnregisterVM(HandlerArg *a) +{ + HRESULT hrc; + const char *VMName = NULL; + bool fDelete = false; + bool fDeleteAll = 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 'a': // --delete-all + fDeleteAll = true; + break; + + case VINF_GETOPT_NOT_OPTION: + if (!VMName) + VMName = ValueUnion.psz; + else + return errorSyntax(Misc::tr("Invalid parameter '%s'"), ValueUnion.psz); + break; + + default: + if (c > 0) + { + if (RT_C_IS_PRINT(c)) + return errorSyntax(Misc::tr("Invalid option -%c"), c); + return errorSyntax(Misc::tr("Invalid option case %i"), c); + } + if (c == VERR_GETOPT_UNKNOWN_OPTION) + return errorSyntax(Misc::tr("unknown option: %s\n"), ValueUnion.psz); + if (ValueUnion.pDef) + return errorSyntax("%s: %Rrs", ValueUnion.pDef->pszLong, c); + return errorSyntax(Misc::tr("error: %Rrs"), c); + } + } + + /* check for required options */ + if (!VMName) + return errorSyntax(Misc::tr("VM name required")); + + ComPtr machine; + CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(VMName).raw(), + machine.asOutParam()), + RTEXITCODE_FAILURE); + SafeIfaceArray aMedia; + CHECK_ERROR_RET(machine, Unregister(fDeleteAll ? CleanupMode_DetachAllReturnHardDisksAndVMRemovable + :CleanupMode_DetachAllReturnHardDisksOnly, + ComSafeArrayAsOutParam(aMedia)), + RTEXITCODE_FAILURE); + if (fDelete || fDeleteAll) + { + ComPtr pProgress; + CHECK_ERROR_RET(machine, DeleteConfig(ComSafeArrayAsInParam(aMedia), pProgress.asOutParam()), + RTEXITCODE_FAILURE); + + hrc = showProgress(pProgress); + CHECK_PROGRESS_ERROR_RET(pProgress, (Misc::tr("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) + hrc = pMedium->Close(); + } + hrc = S_OK; /** @todo r=andy Why overwriting the result from closing the medium above? */ + } + 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 }, + { "--cipher", 'c', RTGETOPT_REQ_STRING }, + { "-cipher", 'c', RTGETOPT_REQ_STRING }, + { "--password-id", 'i', RTGETOPT_REQ_STRING }, + { "-password-id", 'i', RTGETOPT_REQ_STRING }, + { "--password", 'w', RTGETOPT_REQ_STRING }, + { "-password", 'w', RTGETOPT_REQ_STRING }, +}; + +RTEXITCODE handleCreateVM(HandlerArg *a) +{ + HRESULT hrc; + Bstr bstrBaseFolder; + Bstr bstrName; + Bstr bstrOsTypeId; + Bstr bstrUuid; + bool fRegister = false; + bool fDefault = false; + /* TBD. Now not used */ + Bstr bstrDefaultFlags; + com::SafeArray groups; + Bstr bstrCipher; + Bstr bstrPasswordId; + const char *pszPassword = 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_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; + + case 'c': // --cipher + bstrCipher = ValueUnion.psz; + break; + + case 'i': // --password-id + bstrPasswordId = ValueUnion.psz; + break; + + case 'w': // --password + pszPassword = ValueUnion.psz; + break; + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + /* check for required options */ + if (bstrName.isEmpty()) + return errorSyntax(Misc::tr("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())); + Utf8Str strPassword; + if (pszPassword) + { + if (!RTStrCmp(pszPassword, "-")) + { + /* Get password from console. */ + RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Enter the password:"); + if (rcExit == RTEXITCODE_FAILURE) + return rcExit; + } + else + { + RTEXITCODE rcExit = readPasswordFile(pszPassword, &strPassword); + if (rcExit == RTEXITCODE_FAILURE) + { + RTMsgError("Failed to read new password from file"); + return rcExit; + } + } + } + ComPtr machine; + CHECK_ERROR_BREAK(a->virtualBox, + CreateMachine(bstrSettingsFile.raw(), + bstrName.raw(), + ComSafeArrayAsInParam(groups), + bstrOsTypeId.raw(), + createFlags.raw(), + bstrCipher.raw(), + bstrPasswordId.raw(), + Bstr(strPassword).raw(), + machine.asOutParam())); + + CHECK_ERROR_BREAK(machine, SaveSettings()); + if (fDefault) + { + /* ApplyDefaults assumes the machine is already registered */ + CHECK_ERROR_BREAK(machine, ApplyDefaults(bstrDefaultFlags.raw())); + CHECK_ERROR_BREAK(machine, SaveSettings()); + } + if (fRegister) + { + CHECK_ERROR_BREAK(a->virtualBox, RegisterMachine(machine)); + } + + Bstr uuid; + CHECK_ERROR_BREAK(machine, COMGETTER(Id)(uuid.asOutParam())); + Bstr settingsFile; + CHECK_ERROR_BREAK(machine, COMGETTER(SettingsFilePath)(settingsFile.asOutParam())); + RTPrintf(Misc::tr("Virtual machine '%ls' is created%s.\n" + "UUID: %s\n" + "Settings file: '%ls'\n"), + bstrName.raw(), fRegister ? Misc::tr(" and registered") : "", + Utf8Str(uuid).c_str(), settingsFile.raw()); + } + while (0); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +static const RTGETOPTDEF g_aMoveVMOptions[] = +{ + { "--type", 't', RTGETOPT_REQ_STRING }, + { "--folder", 'f', RTGETOPT_REQ_STRING }, +}; + +RTEXITCODE handleMoveVM(HandlerArg *a) +{ + HRESULT hrc; + const char *pszSrcName = NULL; + const char *pszType = NULL; + char szTargetFolder[RTPATH_MAX]; + + 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 + if (ValueUnion.psz && ValueUnion.psz[0] != '\0') + { + vrc = RTPathAbs(ValueUnion.psz, szTargetFolder, sizeof(szTargetFolder)); + if (RT_FAILURE(vrc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("RTPathAbs(%s,,) failed with vrc=%Rrc"), + ValueUnion.psz, vrc); + } else { + szTargetFolder[0] = '\0'; + } + break; + + case VINF_GETOPT_NOT_OPTION: + if (!pszSrcName) + pszSrcName = ValueUnion.psz; + else + return errorSyntax(Misc::tr("Invalid parameter '%s'"), ValueUnion.psz); + break; + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + + if (!pszType) + pszType = "basic"; + + /* Check for required options */ + if (!pszSrcName) + return errorSyntax(Misc::tr("VM name required")); + + /* Get the machine object */ + ComPtr srcMachine; + CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(pszSrcName).raw(), + srcMachine.asOutParam()), + RTEXITCODE_FAILURE); + + if (srcMachine) + { + /* Start the moving */ + ComPtr progress; + + /* we have to open a session for this task */ + CHECK_ERROR_RET(srcMachine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE); + ComPtr sessionMachine; + + CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE); + CHECK_ERROR_RET(sessionMachine, + MoveTo(Bstr(szTargetFolder).raw(), + Bstr(pszType).raw(), + progress.asOutParam()), + RTEXITCODE_FAILURE); + hrc = showProgress(progress); + CHECK_PROGRESS_ERROR_RET(progress, (Misc::tr("Move VM failed")), RTEXITCODE_FAILURE); + + sessionMachine.setNull(); + CHECK_ERROR_RET(a->session, UnlockMachine(), RTEXITCODE_FAILURE); + + RTPrintf(Misc::tr("Machine has been successfully moved into %s\n"), + szTargetFolder[0] != '\0' ? szTargetFolder : Misc::tr("the same location")); + } + + 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 *options) +{ + int vrc = VINF_SUCCESS; + while (psz && *psz && RT_SUCCESS(vrc)) + { + 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 + vrc = VERR_PARSE_ERROR; + } + if (pszComma) + psz += len + 1; + else + psz += len; + } + + return vrc; +} + +RTEXITCODE handleCloneVM(HandlerArg *a) +{ + HRESULT hrc; + const char *pszSrcName = NULL; + const char *pszSnapshotName = NULL; + CloneMode_T mode = CloneMode_MachineState; + com::SafeArray options; + const char *pszTrgName = NULL; + const char *pszTrgBaseFolder = NULL; + bool fRegister = false; + Bstr bstrUuid; + com::SafeArray 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(Misc::tr("Invalid clone mode '%s'\n"), ValueUnion.psz); + break; + + case 'o': // --options + if (RT_FAILURE(parseCloneOptions(ValueUnion.psz, &options))) + return errorArgument(Misc::tr("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(Misc::tr("Invalid parameter '%s'"), ValueUnion.psz); + break; + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + /* Check for required options */ + if (!pszSrcName) + return errorSyntax(Misc::tr("VM name required")); + + /* Get the machine object */ + ComPtr 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 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(Misc::tr("%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 trgMachine; + CHECK_ERROR_RET(a->virtualBox, CreateMachine(bstrSettingsFile.raw(), + Bstr(pszTrgName).raw(), + ComSafeArrayAsInParam(groups), + NULL, + createFlags.raw(), + NULL, + NULL, + NULL, + trgMachine.asOutParam()), + RTEXITCODE_FAILURE); + + /* Start the cloning */ + ComPtr progress; + CHECK_ERROR_RET(srcMachine, CloneTo(trgMachine, + mode, + ComSafeArrayAsInParam(options), + progress.asOutParam()), + RTEXITCODE_FAILURE); + hrc = showProgress(progress); + CHECK_PROGRESS_ERROR_RET(progress, (Misc::tr("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(Misc::tr("Machine has been successfully cloned as \"%ls\"\n"), bstrNewName.raw()); + + return RTEXITCODE_SUCCESS; +} + +RTEXITCODE handleStartVM(HandlerArg *a) +{ + HRESULT hrc = S_OK; + std::list VMs; + Bstr sessionType; + com::SafeArray aBstrEnv; + const char *pszPassword = NULL; + const char *pszPasswordId = NULL; + Utf8Str strPassword; + +#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) + aBstrEnv.push_back(BstrFmt("DISPLAY=%s", pszDisplay).raw()); + const char *pszXAuth = RTEnvGet("XAUTHORITY"); + if (pszXAuth) + aBstrEnv.push_back(BstrFmt("XAUTHORITY=%s", pszXAuth).raw()); + } +#endif + + static const RTGETOPTDEF s_aStartVMOptions[] = + { + { "--type", 't', RTGETOPT_REQ_STRING }, + { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated + { "--putenv", 'E', RTGETOPT_REQ_STRING }, + { "--password", 'p', RTGETOPT_REQ_STRING }, + { "--password-id", 'i', 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")) + aBstrEnv.push_back(Bstr(ValueUnion.psz).raw()); + else + return errorSyntax(Misc::tr("Parameter to option --putenv must not contain any newline character")); + break; + + case 'p': // --password + pszPassword = ValueUnion.psz; + break; + + case 'i': // --password-id + pszPasswordId = ValueUnion.psz; + break; + + case VINF_GETOPT_NOT_OPTION: + VMs.push_back(ValueUnion.psz); + break; + + default: + if (c > 0) + { + if (RT_C_IS_PRINT(c)) + return errorSyntax(Misc::tr("Invalid option -%c"), c); + else + return errorSyntax(Misc::tr("Invalid option case %i"), c); + } + else if (c == VERR_GETOPT_UNKNOWN_OPTION) + return errorSyntax(Misc::tr("unknown option: %s\n"), ValueUnion.psz); + else if (ValueUnion.pDef) + return errorSyntax("%s: %Rrs", ValueUnion.pDef->pszLong, c); + else + return errorSyntax(Misc::tr("error: %Rrs"), c); + } + } + + /* check for required options */ + if (VMs.empty()) + return errorSyntax(Misc::tr("at least one VM name or uuid required")); + + if (pszPassword) + { + if (!RTStrCmp(pszPassword, "-")) + { + /* Get password from console. */ + RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Enter the password:"); + if (rcExit == RTEXITCODE_FAILURE) + return rcExit; + } + else + { + RTEXITCODE rcExit = readPasswordFile(pszPassword, &strPassword); + if (rcExit == RTEXITCODE_FAILURE) + { + RTMsgError("Failed to read new password from file"); + return rcExit; + } + } + } + + for (std::list::const_iterator it = VMs.begin(); + it != VMs.end(); + ++it) + { + HRESULT hrc2 = hrc; + const char *pszVM = *it; + ComPtr machine; + CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszVM).raw(), + machine.asOutParam())); + if (machine) + { + if (pszPasswordId && strPassword.isNotEmpty()) + { + CHECK_ERROR(machine, AddEncryptionPassword(Bstr(pszPasswordId).raw(), Bstr(strPassword).raw())); + if (hrc == VBOX_E_PASSWORD_INCORRECT) + RTMsgError("Password incorrect!"); + } + if (SUCCEEDED(hrc)) + { + ComPtr progress; + CHECK_ERROR(machine, LaunchVMProcess(a->session, sessionType.raw(), + ComSafeArrayAsInParam(aBstrEnv), progress.asOutParam())); + if (SUCCEEDED(hrc) && !progress.isNull()) + { + RTPrintf("Waiting for VM \"%s\" to power on...\n", pszVM); + CHECK_ERROR(progress, WaitForCompletion(-1)); + if (SUCCEEDED(hrc)) + { + BOOL completed = true; + CHECK_ERROR(progress, COMGETTER(Completed)(&completed)); + if (SUCCEEDED(hrc)) + { + ASSERT(completed); + + LONG iRc; + CHECK_ERROR(progress, COMGETTER(ResultCode)(&iRc)); + if (SUCCEEDED(hrc)) + { + if (SUCCEEDED(iRc)) + RTPrintf("VM \"%s\" has been successfully started.\n", pszVM); + else + { + ProgressErrorInfo info(progress); + com::GluePrintErrorInfo(info); + } + hrc = iRc; + } + } + } + } + } + } + + /* it's important to always close sessions */ + a->session->UnlockMachine(); + + /* make sure that we remember the failed state */ + if (FAILED(hrc2)) + hrc = hrc2; + } + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +#ifdef VBOX_WITH_FULL_VM_ENCRYPTION +static const RTGETOPTDEF g_aSetVMEncryptionOptions[] = +{ + { "--new-password", 'n', RTGETOPT_REQ_STRING }, + { "--old-password", 'o', RTGETOPT_REQ_STRING }, + { "--cipher", 'c', RTGETOPT_REQ_STRING }, + { "--new-password-id", 'i', RTGETOPT_REQ_STRING }, + { "--force", 'f', RTGETOPT_REQ_NOTHING}, +}; + +RTEXITCODE handleSetVMEncryption(HandlerArg *a, const char *pszFilenameOrUuid) +{ + HRESULT hrc; + ComPtr machine; + const char *pszPasswordNew = NULL; + const char *pszPasswordOld = NULL; + const char *pszCipher = NULL; + const char *pszNewPasswordId = NULL; + BOOL fForce = FALSE; + 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_aSetVMEncryptionOptions, RT_ELEMENTS(g_aSetVMEncryptionOptions), + 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS); + while ((c = RTGetOpt(&GetState, &ValueUnion))) + { + switch (c) + { + case 'n': // --new-password + pszPasswordNew = ValueUnion.psz; + break; + + case 'o': // --old-password + pszPasswordOld = ValueUnion.psz; + break; + + case 'c': // --cipher + pszCipher = ValueUnion.psz; + break; + + case 'i': // --new-password-id + pszNewPasswordId = ValueUnion.psz; + break; + + case 'f': // --force + fForce = TRUE; + break; + + default: + if (c > 0) + { + if (RT_C_IS_PRINT(c)) + return errorSyntax(Misc::tr("Invalid option -%c"), c); + else + return errorSyntax(Misc::tr("Invalid option case %i"), c); + } + else if (c == VERR_GETOPT_UNKNOWN_OPTION) + return errorSyntax(Misc::tr("unknown option: %s\n"), ValueUnion.psz); + else if (ValueUnion.pDef) + return errorSyntax(Misc::tr("%s: %Rrs"), ValueUnion.pDef->pszLong, c); + else + return errorSyntax(Misc::tr("error: %Rrs"), c); + } + } + + if (!pszFilenameOrUuid) + return errorSyntax(Misc::tr("VM name or UUID required")); + + if (!pszPasswordNew && !pszPasswordOld) + return errorSyntax(Misc::tr("No password specified")); + + if ( (pszPasswordNew && !pszNewPasswordId) + || (!pszPasswordNew && pszNewPasswordId)) + return errorSyntax(Misc::tr("A new password must always have a valid identifier set at the same time")); + + 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; + } + } + } + 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; + } + } + } + + CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszFilenameOrUuid).raw(), + machine.asOutParam())); + if (machine) + { + ComPtr progress; + CHECK_ERROR(machine, ChangeEncryption(Bstr(strPasswordOld).raw(), Bstr(pszCipher).raw(), + Bstr(strPasswordNew).raw(), Bstr(pszNewPasswordId).raw(), + fForce, progress.asOutParam())); + if (SUCCEEDED(hrc)) + hrc = showProgress(progress); + if (FAILED(hrc)) + { + if (hrc == E_NOTIMPL) + RTMsgError("Encrypt VM operation is not implemented!"); + else if (hrc == VBOX_E_NOT_SUPPORTED) + RTMsgError("Encrypt VM operation for this cipher is not implemented yet!"); + else if (!progress.isNull()) + CHECK_PROGRESS_ERROR(progress, ("Failed to encrypt the VM")); + else + RTMsgError("Failed to encrypt the VM!"); + } + } + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +RTEXITCODE handleCheckVMPassword(HandlerArg *a, const char *pszFilenameOrUuid) +{ + HRESULT hrc; + ComPtr machine; + Utf8Str strPassword; + + if (a->argc != 1) + return errorSyntax(Misc::tr("Invalid number of arguments: %d"), a->argc); + + if (!RTStrCmp(a->argv[0], "-")) + { + /* Get password from console. */ + RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Enter the password:"); + if (rcExit == RTEXITCODE_FAILURE) + return rcExit; + } + else + { + RTEXITCODE rcExit = readPasswordFile(a->argv[0], &strPassword); + if (rcExit == RTEXITCODE_FAILURE) + { + RTMsgError("Failed to read password from file"); + return rcExit; + } + } + + CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszFilenameOrUuid).raw(), + machine.asOutParam())); + if (machine) + { + CHECK_ERROR(machine, CheckEncryptionPassword(Bstr(strPassword).raw())); + if (SUCCEEDED(hrc)) + RTPrintf("The given password is correct\n"); + } + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +static const RTGETOPTDEF g_aAddVMOptions[] = +{ + { "--password", 'p', RTGETOPT_REQ_STRING }, + { "--password-id", 'i', RTGETOPT_REQ_STRING } +}; + +RTEXITCODE handleAddVMPassword(HandlerArg *a, const char *pszFilenameOrUuid) +{ + HRESULT hrc; + ComPtr machine; + const char *pszPassword = NULL; + const char *pszPasswordId = NULL; + Utf8Str strPassword; + + 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_aAddVMOptions, RT_ELEMENTS(g_aAddVMOptions), + 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS); + while ((c = RTGetOpt(&GetState, &ValueUnion))) + { + switch (c) + { + case 'p': // --password + pszPassword = ValueUnion.psz; + break; + + case 'i': // --password-id + pszPasswordId = ValueUnion.psz; + break; + + default: + if (c > 0) + { + if (RT_C_IS_PRINT(c)) + return errorSyntax(Misc::tr("Invalid option -%c"), c); + else + return errorSyntax(Misc::tr("Invalid option case %i"), c); + } + else if (c == VERR_GETOPT_UNKNOWN_OPTION) + return errorSyntax(Misc::tr("unknown option: %s\n"), ValueUnion.psz); + else if (ValueUnion.pDef) + return errorSyntax(Misc::tr("%s: %Rrs"), ValueUnion.pDef->pszLong, c); + else + return errorSyntax(Misc::tr("error: %Rrs"), c); + } + } + + if (!pszFilenameOrUuid) + return errorSyntax(Misc::tr("VM name or UUID required")); + + if (!pszPassword) + return errorSyntax(Misc::tr("No password specified")); + + if (!pszPasswordId) + return errorSyntax(Misc::tr("No password identifier specified")); + + if (!RTStrCmp(pszPassword, "-")) + { + /* Get password from console. */ + RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Enter the password:"); + if (rcExit == RTEXITCODE_FAILURE) + return rcExit; + } + else + { + RTEXITCODE rcExit = readPasswordFile(pszPassword, &strPassword); + if (rcExit == RTEXITCODE_FAILURE) + { + RTMsgError("Failed to read new password from file"); + return rcExit; + } + } + + CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszFilenameOrUuid).raw(), + machine.asOutParam())); + if (machine) + { + ComPtr progress; + CHECK_ERROR(machine, AddEncryptionPassword(Bstr(pszPasswordId).raw(), Bstr(strPassword).raw())); + if (hrc == VBOX_E_PASSWORD_INCORRECT) + RTMsgError("Password incorrect!"); + } + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +RTEXITCODE handleRemoveVMPassword(HandlerArg *a, const char *pszFilenameOrUuid) +{ + HRESULT hrc; + ComPtr machine; + + if (a->argc != 1) + return errorSyntax(Misc::tr("Invalid number of arguments: %d"), a->argc); + + CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszFilenameOrUuid).raw(), + machine.asOutParam())); + if (machine) + { + CHECK_ERROR(machine, RemoveEncryptionPassword(Bstr(a->argv[0]).raw())); + if (hrc == VBOX_E_INVALID_VM_STATE) + RTMsgError("The machine is in online or transient state\n"); + } + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +RTEXITCODE handleEncryptVM(HandlerArg *a) +{ + if (a->argc < 2) + return errorSyntax(Misc::tr("subcommand required")); + + HandlerArg handlerArg; + handlerArg.argc = a->argc - 2; + handlerArg.argv = &a->argv[2]; + handlerArg.virtualBox = a->virtualBox; + handlerArg.session = a->session; + if (!strcmp(a->argv[1], "setencryption")) + return handleSetVMEncryption(&handlerArg, a->argv[0]); + if (!strcmp(a->argv[1], "checkpassword")) + return handleCheckVMPassword(&handlerArg, a->argv[0]); + if (!strcmp(a->argv[1], "addpassword")) + return handleAddVMPassword(&handlerArg, a->argv[0]); + if (!strcmp(a->argv[1], "removepassword")) + return handleRemoveVMPassword(&handlerArg, a->argv[0]); + return errorSyntax(Misc::tr("unknown subcommand")); +} +#endif /* !VBOX_WITH_FULL_VM_ENCRYPTION */ + +RTEXITCODE handleDiscardState(HandlerArg *a) +{ + HRESULT hrc; + + if (a->argc != 1) + return errorSyntax(Misc::tr("Incorrect number of parameters")); + + ComPtr 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 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(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +RTEXITCODE handleAdoptState(HandlerArg *a) +{ + HRESULT hrc; + + if (a->argc != 2) + return errorSyntax(Misc::tr("Incorrect number of parameters")); + + ComPtr 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, Misc::tr("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 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(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +RTEXITCODE handleGetExtraData(HandlerArg *a) +{ + HRESULT hrc = S_OK; + + if (a->argc > 2 || a->argc < 1) + return errorSyntax(Misc::tr("Incorrect number of parameters")); + + /* global data? */ + if (!strcmp(a->argv[0], "global")) + { + /* enumeration? */ + if (a->argc < 2 || !strcmp(a->argv[1], "enumerate")) + { + SafeArray 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(Misc::tr("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(Misc::tr("Value: %ls\n"), value.raw()); + else + RTPrintf(Misc::tr("No value set!\n")); + } + } + else + { + ComPtr 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 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(Misc::tr("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(Misc::tr("Value: %ls\n"), value.raw()); + else + RTPrintf(Misc::tr("No value set!\n")); + } + } + } + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +RTEXITCODE handleSetExtraData(HandlerArg *a) +{ + HRESULT hrc = S_OK; + + if (a->argc < 2) + return errorSyntax(Misc::tr("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(Misc::tr("Too many parameters")); + } + else + { + ComPtr 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 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(Misc::tr("Too many parameters")); + } + } + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +RTEXITCODE handleSetProperty(HandlerArg *a) +{ + HRESULT hrc; + + /* there must be two arguments: property name and value */ + if (a->argc != 2) + return errorSyntax(Misc::tr("Incorrect number of parameters")); + + ComPtr 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(Misc::tr("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, Misc::tr("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(Misc::tr("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(Misc::tr("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())); + } +#ifdef VBOX_WITH_MAIN_NLS + else if (!strcmp(a->argv[0], "language")) + { + Bstr bstrLanguage(a->argv[1]); + CHECK_ERROR(systemProperties, COMSETTER(LanguageId)(bstrLanguage.raw())); + + /* Kudge alert! Make sure the language change notification is processed, + otherwise it may arrive as (XP)COM shuts down and cause + trouble in debug builds. */ +# ifdef DEBUG + uint64_t const tsStart = RTTimeNanoTS(); +# endif + unsigned cMsgs = 0; + int vrc; + while ( RT_SUCCESS(vrc = NativeEventQueue::getMainEventQueue()->processEventQueue(32 /*ms*/)) + || vrc == VERR_INTERRUPTED) + cMsgs++; +# ifdef DEBUG + RTPrintf("vrc=%Rrc cMsgs=%u nsElapsed=%'RU64\n", vrc, cMsgs, RTTimeNanoTS() - tsStart); +# endif + } +#endif + else + return errorSyntax(Misc::tr("Invalid parameter '%s'"), a->argv[0]); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +/** + * sharedfolder add + */ +static RTEXITCODE handleSharedFolderAdd(HandlerArg *a) +{ + /* + * Parse arguments (argv[0] == subcommand). + */ + static const RTGETOPTDEF s_aAddOptions[] = + { + { "--name", 'n', RTGETOPT_REQ_STRING }, + { "-name", 'n', RTGETOPT_REQ_STRING }, // deprecated + { "--hostpath", 'p', RTGETOPT_REQ_STRING }, + { "-hostpath", 'p', RTGETOPT_REQ_STRING }, // deprecated + { "--readonly", 'r', RTGETOPT_REQ_NOTHING }, + { "-readonly", 'r', RTGETOPT_REQ_NOTHING }, // deprecated + { "--transient", 't', RTGETOPT_REQ_NOTHING }, + { "-transient", 't', RTGETOPT_REQ_NOTHING }, // deprecated + { "--automount", 'a', RTGETOPT_REQ_NOTHING }, + { "-automount", 'a', RTGETOPT_REQ_NOTHING }, // deprecated + { "--auto-mount-point", 'm', RTGETOPT_REQ_STRING }, + }; + const char *pszMachineName = NULL; + const char *pszName = NULL; + const char *pszHostPath = NULL; + bool fTransient = false; + bool fWritable = true; + bool fAutoMount = false; + const char *pszAutoMountPoint = ""; + + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, a->argc, a->argv, s_aAddOptions, RT_ELEMENTS(s_aAddOptions), 1 /*iFirst*/, 0 /*fFlags*/); + int c; + RTGETOPTUNION ValueUnion; + while ((c = RTGetOpt(&GetState, &ValueUnion))) + { + switch (c) + { + case 'n': + pszName = ValueUnion.psz; + break; + case 'p': + pszHostPath = ValueUnion.psz; + break; + case 'r': + fWritable = false; + break; + case 't': + fTransient = true; + break; + case 'a': + fAutoMount = true; + break; + case 'm': + pszAutoMountPoint = ValueUnion.psz; + break; + case VINF_GETOPT_NOT_OPTION: + if (pszMachineName) + return errorArgument(Misc::tr("Machine name is given more than once: first '%s', then '%s'"), + pszMachineName, ValueUnion.psz); + pszMachineName = ValueUnion.psz; + break; + default: + return errorGetOpt(c, &ValueUnion); + } + } + + if (!pszMachineName) + return errorSyntax(Misc::tr("No machine was specified")); + + if (!pszName) + return errorSyntax(Misc::tr("No shared folder name (--name) was given")); + if (strchr(pszName, ' ')) + return errorSyntax(Misc::tr("Invalid shared folder name '%s': contains space"), pszName); + if (strchr(pszName, '\t')) + return errorSyntax(Misc::tr("Invalid shared folder name '%s': contains tabs"), pszName); + if (strchr(pszName, '\n') || strchr(pszName, '\r')) + return errorSyntax(Misc::tr("Invalid shared folder name '%s': contains newline"), pszName); + + if (!pszHostPath) + return errorSyntax(Misc::tr("No host path (--hostpath) was given")); + char szAbsHostPath[RTPATH_MAX]; + int vrc = RTPathAbs(pszHostPath, szAbsHostPath, sizeof(szAbsHostPath)); + if (RT_FAILURE(vrc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("RTAbsPath failed on '%s': %Rrc"), pszHostPath, vrc); + + /* + * Done parsing, do some work. + */ + ComPtr ptrMachine; + CHECK_ERROR2I_RET(a->virtualBox, FindMachine(Bstr(pszMachineName).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE); + AssertReturn(ptrMachine.isNotNull(), RTEXITCODE_FAILURE); + + HRESULT hrc; + if (fTransient) + { + /* open an existing session for the VM */ + CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE); + + /* get the session machine */ + ComPtr ptrSessionMachine; + CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE); + + /* get the session console */ + ComPtr ptrConsole; + CHECK_ERROR2I_RET(a->session, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE); + if (ptrConsole.isNull()) + return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Machine '%s' is not currently running."), pszMachineName); + + CHECK_ERROR2(hrc, ptrConsole, CreateSharedFolder(Bstr(pszName).raw(), Bstr(szAbsHostPath).raw(), + fWritable, fAutoMount, Bstr(pszAutoMountPoint).raw())); + a->session->UnlockMachine(); + } + else + { + /* open a session for the VM */ + CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE); + + /* get the mutable session machine */ + ComPtr ptrSessionMachine; + CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE); + + CHECK_ERROR2(hrc, ptrSessionMachine, CreateSharedFolder(Bstr(pszName).raw(), Bstr(szAbsHostPath).raw(), + fWritable, fAutoMount, Bstr(pszAutoMountPoint).raw())); + if (SUCCEEDED(hrc)) + { + CHECK_ERROR2(hrc, ptrSessionMachine, SaveSettings()); + } + + a->session->UnlockMachine(); + } + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +/** + * sharedfolder remove + */ +static RTEXITCODE handleSharedFolderRemove(HandlerArg *a) +{ + /* + * Parse arguments (argv[0] == subcommand). + */ + static const RTGETOPTDEF s_aRemoveOptions[] = + { + { "--name", 'n', RTGETOPT_REQ_STRING }, + { "-name", 'n', RTGETOPT_REQ_STRING }, // deprecated + { "--transient", 't', RTGETOPT_REQ_NOTHING }, + { "-transient", 't', RTGETOPT_REQ_NOTHING }, // deprecated + }; + const char *pszMachineName = NULL; + const char *pszName = NULL; + bool fTransient = false; + + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, a->argc, a->argv, s_aRemoveOptions, RT_ELEMENTS(s_aRemoveOptions), 1 /*iFirst*/, 0 /*fFlags*/); + int c; + RTGETOPTUNION ValueUnion; + while ((c = RTGetOpt(&GetState, &ValueUnion))) + { + switch (c) + { + case 'n': + pszName = ValueUnion.psz; + break; + case 't': + fTransient = true; + break; + case VINF_GETOPT_NOT_OPTION: + if (pszMachineName) + return errorArgument(Misc::tr("Machine name is given more than once: first '%s', then '%s'"), + pszMachineName, ValueUnion.psz); + pszMachineName = ValueUnion.psz; + break; + default: + return errorGetOpt(c, &ValueUnion); + } + } + + if (!pszMachineName) + return errorSyntax(Misc::tr("No machine was specified")); + if (!pszName) + return errorSyntax(Misc::tr("No shared folder name (--name) was given")); + + /* + * Done parsing, do some real work. + */ + ComPtr ptrMachine; + CHECK_ERROR2I_RET(a->virtualBox, FindMachine(Bstr(pszMachineName).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE); + AssertReturn(ptrMachine.isNotNull(), RTEXITCODE_FAILURE); + + HRESULT hrc; + if (fTransient) + { + /* open an existing session for the VM */ + CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE); + /* get the session machine */ + ComPtr ptrSessionMachine; + CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE); + /* get the session console */ + ComPtr ptrConsole; + CHECK_ERROR2I_RET(a->session, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE); + if (ptrConsole.isNull()) + return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Machine '%s' is not currently running.\n"), pszMachineName); + + CHECK_ERROR2(hrc, ptrConsole, RemoveSharedFolder(Bstr(pszName).raw())); + + a->session->UnlockMachine(); + } + else + { + /* open a session for the VM */ + CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE); + + /* get the mutable session machine */ + ComPtr ptrSessionMachine; + CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE); + + CHECK_ERROR2(hrc, ptrSessionMachine, RemoveSharedFolder(Bstr(pszName).raw())); + + /* commit and close the session */ + if (SUCCEEDED(hrc)) + { + CHECK_ERROR2(hrc, ptrSessionMachine, SaveSettings()); + } + a->session->UnlockMachine(); + } + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + + +RTEXITCODE handleSharedFolder(HandlerArg *a) +{ + if (a->argc < 1) + return errorSyntax(Misc::tr("Not enough parameters")); + + if (!strcmp(a->argv[0], "add")) + { + setCurrentSubcommand(HELP_SCOPE_SHAREDFOLDER_ADD); + return handleSharedFolderAdd(a); + } + + if (!strcmp(a->argv[0], "remove")) + { + setCurrentSubcommand(HELP_SCOPE_SHAREDFOLDER_REMOVE); + return handleSharedFolderRemove(a); + } + + return errorUnknownSubcommand(a->argv[0]); +} + +RTEXITCODE handleExtPack(HandlerArg *a) +{ + if (a->argc < 1) + return errorNoSubcommand(); + + ComObjPtr 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 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(Misc::tr("Too many extension pack names given to \"extpack uninstall\"")); + pszName = ValueUnion.psz; + break; + + default: + return errorGetOpt(ch, &ValueUnion); + } + } + if (!pszName) + return errorSyntax(Misc::tr("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, Misc::tr("RTPathAbs(%s,,) failed with vrc=%Rrc"), pszName, vrc); + + Bstr bstrTarball(szPath); + Bstr bstrName; + ComPtr 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(Misc::tr("License accepted.\n")); + else + { + RTPrintf("%s\n", strLicense.c_str()); + RTPrintf(Misc::tr("Do you agree to these license terms and conditions (y/n)? ")); + ch = RTStrmGetCh(g_pStdIn); + RTPrintf("\n"); + if (ch != 'y' && ch != 'Y') + { + RTPrintf(Misc::tr("Installation of \"%ls\" aborted.\n"), bstrName.raw()); + return RTEXITCODE_FAILURE; + } + if (szDigest[0]) + RTPrintf(Misc::tr("License accepted. For batch installation add\n" + "--accept-license=%s\n" + "to the VBoxManage command line.\n\n"), szDigest); + } + } + ComPtr ptrProgress; + CHECK_ERROR2I_RET(ptrExtPackFile, Install(fReplace, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE); + hrc = showProgress(ptrProgress); + CHECK_PROGRESS_ERROR_RET(ptrProgress, (Misc::tr("Failed to install \"%s\""), szPath), RTEXITCODE_FAILURE); + + RTPrintf(Misc::tr("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(Misc::tr("Too many extension pack names given to \"extpack uninstall\"")); + pszName = ValueUnion.psz; + break; + + default: + return errorGetOpt(ch, &ValueUnion); + } + } + if (!pszName) + return errorSyntax(Misc::tr("No extension pack name was given to \"extpack uninstall\"")); + + Bstr bstrName(pszName); + ComPtr ptrProgress; + CHECK_ERROR2I_RET(ptrExtPackMgr, Uninstall(bstrName.raw(), fForced, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE); + hrc = showProgress(ptrProgress); + CHECK_PROGRESS_ERROR_RET(ptrProgress, (Misc::tr("Failed to uninstall \"%s\""), pszName), RTEXITCODE_FAILURE); + + RTPrintf(Misc::tr("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(Misc::tr("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(Misc::tr("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(Misc::tr("No ISO specified")); + + /* + * Do the job. + */ + ComPtr 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); + SafeArray aImageNames; + CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedImageNames)(ComSafeArrayAsOutParam(aImageNames)), RTEXITCODE_FAILURE); + SafeArray aImageIndices; + CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedImageIndices)(ComSafeArrayAsOutParam(aImageIndices)), RTEXITCODE_FAILURE); + Assert(aImageNames.size() == aImageIndices.size()); + BOOL fInstallSupported = FALSE; + CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(IsUnattendedInstallSupported)(&fInstallSupported), RTEXITCODE_FAILURE); + + if (fMachineReadable) + { + outputMachineReadableString("OSTypeId", &bstrDetectedOSTypeId); + outputMachineReadableString("OSVersion", &bstrDetectedVersion); + outputMachineReadableString("OSFlavor", &bstrDetectedFlavor); + outputMachineReadableString("OSLanguages", &bstrDetectedLanguages); + outputMachineReadableString("OSHints", &bstrDetectedHints); + for (size_t i = 0; i < aImageNames.size(); i++) + { + Bstr const bstrName = aImageNames[i]; + outputMachineReadableStringWithFmtName(&bstrName, false, "ImageIndex%u", aImageIndices[i]); + } + outputMachineReadableBool("IsInstallSupported", &fInstallSupported); + } + else + { + RTMsgInfo(Misc::tr("Detected '%s' to be:\n"), szIsoPath); + RTPrintf(Misc::tr(" 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()); + for (size_t i = 0; i < aImageNames.size(); i++) + RTPrintf(" Image #%-3u = %ls\n", aImageIndices[i], aImageNames[i]); + if (fInstallSupported) + RTPrintf(Misc::tr(" Unattended installation supported = yes\n")); + else + RTPrintf(Misc::tr(" Unattended installation supported = no\n")); + } + + return rcExit; +} + +RTEXITCODE handleUnattendedInstall(HandlerArg *a) +{ + HRESULT hrc; + char szAbsPath[RTPATH_MAX]; + + /* + * Options. We work directly on an IUnattended instance while parsing + * the options. This saves a lot of extra clutter. + */ + ComPtr ptrUnattended; + CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE); + RTCList arrPackageSelectionAdjustments; + ComPtr 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(Misc::tr("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(Misc::tr("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(Misc::tr("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(Misc::tr("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(Misc::tr("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(Misc::tr("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(Misc::tr("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(Misc::tr("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 ptrConsole; + CHECK_ERROR2(hrc, a->session, COMGETTER(Console)(ptrConsole.asOutParam())); + + if ( ptrConsole.isNull() + && SUCCEEDED(hrc) + && ( RTStrICmp(pszSessionType, "gui") == 0 + || RTStrICmp(pszSessionType, "none") == 0)) + { + ComPtr 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, Misc::tr("Machine '%ls' is currently running"), bstrMachineName.raw()); + } + + /* + * Do the work. + */ + RTMsgInfo(Misc::tr("%s unattended installation of %s in machine '%ls' (%ls).\n"), + RTStrICmp(pszSessionType, "none") == 0 ? Misc::tr("Preparing") : Misc::tr("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(Misc::tr("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(Misc::tr(" %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(Misc::tr(" %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"); + { + ULONG idxImage = 0; + HRESULT hrc2 = ptrUnattended->COMGETTER(ImageIndex)(&idxImage); + if (FAILED(hrc2)) + idxImage = 0; + SafeArray aImageNames; + hrc2 = ptrUnattended->COMGETTER(DetectedImageNames)(ComSafeArrayAsOutParam(aImageNames)); + if (SUCCEEDED(hrc2)) + { + SafeArray aImageIndices; + hrc2 = ptrUnattended->COMGETTER(DetectedImageIndices)(ComSafeArrayAsOutParam(aImageIndices)); + if (SUCCEEDED(hrc2)) + { + Assert(aImageNames.size() == aImageIndices.size()); + for (size_t i = 0; i < aImageNames.size(); i++) + { + char szTmp[64]; + RTStrPrintf(szTmp, sizeof(szTmp), "detectedImage[%u]%s", i, idxImage != aImageIndices[i] ? "" : "*"); + RTPrintf(" %32s = #%u: %ls\n", szTmp, aImageIndices[i], aImageNames[i]); + } + } + else + RTPrintf(Misc::tr(" %32s = failed: %Rhrc\n"), "detectedImageIndices", hrc2); + } + else + RTPrintf(Misc::tr(" %32 = failed: %Rhrc\n"), "detectedImageNames", hrc2); + } + +#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(Misc::tr("VM '%ls' (%ls) is ready to be started (e.g. VBoxManage startvm).\n"), bstrMachineName.raw(), bstrUuid.raw()); + hrc = S_OK; + } + else + { + com::SafeArray aBstrEnv; +#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS) + /* make sure the VM process will start on the same display as VBoxManage */ + const char *pszDisplay = RTEnvGet("DISPLAY"); + if (pszDisplay) + aBstrEnv.push_back(BstrFmt("DISPLAY=%s", pszDisplay).raw()); + const char *pszXAuth = RTEnvGet("XAUTHORITY"); + if (pszXAuth) + aBstrEnv.push_back(BstrFmt("XAUTHORITY=%s", pszXAuth).raw()); +#endif + ComPtr ptrProgress; + CHECK_ERROR2(hrc, ptrMachine, LaunchVMProcess(a->session, Bstr(pszSessionType).raw(), ComSafeArrayAsInParam(aBstrEnv), ptrProgress.asOutParam())); + if (SUCCEEDED(hrc) && !ptrProgress.isNull()) + { + RTMsgInfo(Misc::tr("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(Misc::tr("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]); +} + +/** + * Common Cloud profile options. + */ +typedef struct +{ + const char *pszProviderName; + const char *pszProfileName; +} CLOUDPROFILECOMMONOPT; +typedef CLOUDPROFILECOMMONOPT *PCLOUDPROFILECOMMONOPT; + +/** + * Sets the properties of cloud profile + * + * @returns 0 on success, 1 on failure + */ + +static RTEXITCODE setCloudProfileProperties(HandlerArg *a, int iFirst, PCLOUDPROFILECOMMONOPT pCommonOpts) +{ + + HRESULT hrc = S_OK; + + Bstr bstrProvider(pCommonOpts->pszProviderName); + Bstr bstrProfile(pCommonOpts->pszProfileName); + + /* + * Parse options. + */ + static const RTGETOPTDEF s_aOptions[] = + { + { "--clouduser", 'u', RTGETOPT_REQ_STRING }, + { "--fingerprint", 'p', RTGETOPT_REQ_STRING }, + { "--keyfile", 'k', RTGETOPT_REQ_STRING }, + { "--passphrase", 'P', RTGETOPT_REQ_STRING }, + { "--tenancy", 't', RTGETOPT_REQ_STRING }, + { "--compartment", 'c', RTGETOPT_REQ_STRING }, + { "--region", 'r', RTGETOPT_REQ_STRING } + }; + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + com::SafeArray names; + com::SafeArray values; + + int c; + RTGETOPTUNION ValueUnion; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'u': // --clouduser + Bstr("user").detachTo(names.appendedRaw()); + Bstr(ValueUnion.psz).detachTo(values.appendedRaw()); + break; + case 'p': // --fingerprint + Bstr("fingerprint").detachTo(names.appendedRaw()); + Bstr(ValueUnion.psz).detachTo(values.appendedRaw()); + break; + case 'k': // --keyfile + Bstr("key_file").detachTo(names.appendedRaw()); + Bstr(ValueUnion.psz).detachTo(values.appendedRaw()); + break; + case 'P': // --passphrase + Bstr("pass_phrase").detachTo(names.appendedRaw()); + Bstr(ValueUnion.psz).detachTo(values.appendedRaw()); + break; + case 't': // --tenancy + Bstr("tenancy").detachTo(names.appendedRaw()); + Bstr(ValueUnion.psz).detachTo(values.appendedRaw()); + break; + case 'c': // --compartment + Bstr("compartment").detachTo(names.appendedRaw()); + Bstr(ValueUnion.psz).detachTo(values.appendedRaw()); + break; + case 'r': // --region + Bstr("region").detachTo(names.appendedRaw()); + Bstr(ValueUnion.psz).detachTo(values.appendedRaw()); + break; + default: + return errorGetOpt(c, &ValueUnion); + } + } + + /* check for required options */ + if (bstrProvider.isEmpty()) + return errorSyntax(Misc::tr("Parameter --provider is required")); + if (bstrProfile.isEmpty()) + return errorSyntax(Misc::tr("Parameter --profile is required")); + + ComPtr pVirtualBox = a->virtualBox; + + ComPtr pCloudProviderManager; + CHECK_ERROR2_RET(hrc, pVirtualBox, + COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()), + RTEXITCODE_FAILURE); + + ComPtr pCloudProvider; + + CHECK_ERROR2_RET(hrc, pCloudProviderManager, + GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()), + RTEXITCODE_FAILURE); + + ComPtr pCloudProfile; + + if (pCloudProvider) + { + CHECK_ERROR2_RET(hrc, pCloudProvider, + GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()), + RTEXITCODE_FAILURE); + CHECK_ERROR2_RET(hrc, pCloudProfile, + SetProperties(ComSafeArrayAsInParam(names), ComSafeArrayAsInParam(values)), + RTEXITCODE_FAILURE); + } + + CHECK_ERROR2(hrc, pCloudProvider, SaveProfiles()); + + RTPrintf(Misc::tr("Provider %ls: profile '%ls' was updated.\n"),bstrProvider.raw(), bstrProfile.raw()); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +/** + * Gets the properties of cloud profile + * + * @returns 0 on success, 1 on failure + */ +static RTEXITCODE showCloudProfileProperties(HandlerArg *a, PCLOUDPROFILECOMMONOPT pCommonOpts) +{ + HRESULT hrc = S_OK; + + Bstr bstrProvider(pCommonOpts->pszProviderName); + Bstr bstrProfile(pCommonOpts->pszProfileName); + + /* check for required options */ + if (bstrProvider.isEmpty()) + return errorSyntax(Misc::tr("Parameter --provider is required")); + if (bstrProfile.isEmpty()) + return errorSyntax(Misc::tr("Parameter --profile is required")); + + ComPtr pVirtualBox = a->virtualBox; + ComPtr pCloudProviderManager; + CHECK_ERROR2_RET(hrc, pVirtualBox, + COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()), + RTEXITCODE_FAILURE); + ComPtr pCloudProvider; + CHECK_ERROR2_RET(hrc, pCloudProviderManager, + GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()), + RTEXITCODE_FAILURE); + + ComPtr pCloudProfile; + if (pCloudProvider) + { + CHECK_ERROR2_RET(hrc, pCloudProvider, + GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()), + RTEXITCODE_FAILURE); + + Bstr bstrProviderID; + pCloudProfile->COMGETTER(ProviderId)(bstrProviderID.asOutParam()); + RTPrintf(Misc::tr("Provider GUID: %ls\n"), bstrProviderID.raw()); + + com::SafeArray names; + com::SafeArray values; + CHECK_ERROR2_RET(hrc, pCloudProfile, + GetProperties(Bstr().raw(), ComSafeArrayAsOutParam(names), ComSafeArrayAsOutParam(values)), + RTEXITCODE_FAILURE); + 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 ? Misc::tr("Property: ") : " ", + names[k], value.raw()); + fFirst = false; + } + + RTPrintf("\n"); + } + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +static RTEXITCODE addCloudProfile(HandlerArg *a, int iFirst, PCLOUDPROFILECOMMONOPT pCommonOpts) +{ + HRESULT hrc = S_OK; + + Bstr bstrProvider(pCommonOpts->pszProviderName); + Bstr bstrProfile(pCommonOpts->pszProfileName); + + + /* check for required options */ + if (bstrProvider.isEmpty()) + return errorSyntax(Misc::tr("Parameter --provider is required")); + if (bstrProfile.isEmpty()) + return errorSyntax(Misc::tr("Parameter --profile is required")); + + /* + * Parse options. + */ + static const RTGETOPTDEF s_aOptions[] = + { + { "--clouduser", 'u', RTGETOPT_REQ_STRING }, + { "--fingerprint", 'p', RTGETOPT_REQ_STRING }, + { "--keyfile", 'k', RTGETOPT_REQ_STRING }, + { "--passphrase", 'P', RTGETOPT_REQ_STRING }, + { "--tenancy", 't', RTGETOPT_REQ_STRING }, + { "--compartment", 'c', RTGETOPT_REQ_STRING }, + { "--region", 'r', RTGETOPT_REQ_STRING } + }; + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + com::SafeArray names; + com::SafeArray values; + + int c; + RTGETOPTUNION ValueUnion; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'u': // --clouduser + Bstr("user").detachTo(names.appendedRaw()); + Bstr(ValueUnion.psz).detachTo(values.appendedRaw()); + break; + case 'p': // --fingerprint + Bstr("fingerprint").detachTo(names.appendedRaw()); + Bstr(ValueUnion.psz).detachTo(values.appendedRaw()); + break; + case 'k': // --keyfile + Bstr("key_file").detachTo(names.appendedRaw()); + Bstr(ValueUnion.psz).detachTo(values.appendedRaw()); + break; + case 'P': // --passphrase + Bstr("pass_phrase").detachTo(names.appendedRaw()); + Bstr(ValueUnion.psz).detachTo(values.appendedRaw()); + break; + case 't': // --tenancy + Bstr("tenancy").detachTo(names.appendedRaw()); + Bstr(ValueUnion.psz).detachTo(values.appendedRaw()); + break; + case 'c': // --compartment + Bstr("compartment").detachTo(names.appendedRaw()); + Bstr(ValueUnion.psz).detachTo(values.appendedRaw()); + break; + case 'r': // --region + Bstr("region").detachTo(names.appendedRaw()); + Bstr(ValueUnion.psz).detachTo(values.appendedRaw()); + break; + default: + return errorGetOpt(c, &ValueUnion); + } + } + + ComPtr pVirtualBox = a->virtualBox; + + ComPtr pCloudProviderManager; + CHECK_ERROR2_RET(hrc, pVirtualBox, + COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()), + RTEXITCODE_FAILURE); + + ComPtr pCloudProvider; + CHECK_ERROR2_RET(hrc, pCloudProviderManager, + GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()), + RTEXITCODE_FAILURE); + + CHECK_ERROR2_RET(hrc, pCloudProvider, + CreateProfile(bstrProfile.raw(), + ComSafeArrayAsInParam(names), + ComSafeArrayAsInParam(values)), + RTEXITCODE_FAILURE); + + CHECK_ERROR2(hrc, pCloudProvider, SaveProfiles()); + + RTPrintf(Misc::tr("Provider %ls: profile '%ls' was added.\n"),bstrProvider.raw(), bstrProfile.raw()); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +static RTEXITCODE deleteCloudProfile(HandlerArg *a, PCLOUDPROFILECOMMONOPT pCommonOpts) +{ + HRESULT hrc = S_OK; + + Bstr bstrProvider(pCommonOpts->pszProviderName); + Bstr bstrProfile(pCommonOpts->pszProfileName); + + /* check for required options */ + if (bstrProvider.isEmpty()) + return errorSyntax(Misc::tr("Parameter --provider is required")); + if (bstrProfile.isEmpty()) + return errorSyntax(Misc::tr("Parameter --profile is required")); + + ComPtr pVirtualBox = a->virtualBox; + ComPtr pCloudProviderManager; + CHECK_ERROR2_RET(hrc, pVirtualBox, + COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()), + RTEXITCODE_FAILURE); + ComPtr pCloudProvider; + CHECK_ERROR2_RET(hrc, pCloudProviderManager, + GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()), + RTEXITCODE_FAILURE); + + ComPtr pCloudProfile; + if (pCloudProvider) + { + CHECK_ERROR2_RET(hrc, pCloudProvider, + GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()), + RTEXITCODE_FAILURE); + + CHECK_ERROR2_RET(hrc, pCloudProfile, + Remove(), + RTEXITCODE_FAILURE); + + CHECK_ERROR2_RET(hrc, pCloudProvider, + SaveProfiles(), + RTEXITCODE_FAILURE); + + RTPrintf(Misc::tr("Provider %ls: profile '%ls' was deleted.\n"), bstrProvider.raw(), bstrProfile.raw()); + } + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +RTEXITCODE handleCloudProfile(HandlerArg *a) +{ + if (a->argc < 1) + return errorNoSubcommand(); + + static const RTGETOPTDEF s_aOptions[] = + { + /* common options */ + { "--provider", 'v', RTGETOPT_REQ_STRING }, + { "--profile", 'f', RTGETOPT_REQ_STRING }, + /* subcommands */ + { "add", 1000, RTGETOPT_REQ_NOTHING }, + { "show", 1001, RTGETOPT_REQ_NOTHING }, + { "update", 1002, RTGETOPT_REQ_NOTHING }, + { "delete", 1003, RTGETOPT_REQ_NOTHING }, + }; + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + CLOUDPROFILECOMMONOPT CommonOpts = { NULL, NULL }; + int c; + RTGETOPTUNION ValueUnion; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'v': // --provider + CommonOpts.pszProviderName = ValueUnion.psz; + break; + case 'f': // --profile + CommonOpts.pszProfileName = ValueUnion.psz; + break; + /* Sub-commands: */ + case 1000: + setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_ADD); + return addCloudProfile(a, GetState.iNext, &CommonOpts); + case 1001: + setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_SHOW); + return showCloudProfileProperties(a, &CommonOpts); + case 1002: + setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_UPDATE); + return setCloudProfileProperties(a, GetState.iNext, &CommonOpts); + case 1003: + setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_DELETE); + return deleteCloudProfile(a, &CommonOpts); + case VINF_GETOPT_NOT_OPTION: + return errorUnknownSubcommand(ValueUnion.psz); + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + return errorNoSubcommand(); +} diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageModifyNvram.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageModifyNvram.cpp new file mode 100644 index 00000000..d36bed08 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageModifyNvram.cpp @@ -0,0 +1,582 @@ +/* $Id: VBoxManageModifyNvram.cpp $ */ +/** @file + * VBoxManage - The nvram control related commands. + */ + +/* + * Copyright (C) 2021-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "VBoxManage.h" +using namespace com; + +DECLARE_TRANSLATION_CONTEXT(Nvram); + +// funcs +/////////////////////////////////////////////////////////////////////////////// + + +/** + * Handles the 'modifynvram myvm inituefivarstore' sub-command. + * @returns Exit code. + * @param a The handler argument package. + * @param nvram Reference to the NVRAM store interface. + */ +static RTEXITCODE handleModifyNvramInitUefiVarStore(HandlerArg *a, ComPtr &nvramStore) +{ + RT_NOREF(a); + + CHECK_ERROR2I_RET(nvramStore, InitUefiVariableStore(0 /*aSize*/), RTEXITCODE_FAILURE); + return RTEXITCODE_SUCCESS; +} + + +/** + * Handles the 'modifynvram myvm enrollmssignatures' sub-command. + * @returns Exit code. + * @param a The handler argument package. + * @param nvram Reference to the NVRAM store interface. + */ +static RTEXITCODE handleModifyNvramEnrollMsSignatures(HandlerArg *a, ComPtr &nvramStore) +{ + RT_NOREF(a); + + ComPtr uefiVarStore; + CHECK_ERROR2I_RET(nvramStore, COMGETTER(UefiVariableStore)(uefiVarStore.asOutParam()), RTEXITCODE_FAILURE); + + CHECK_ERROR2I_RET(uefiVarStore, EnrollDefaultMsSignatures(), RTEXITCODE_FAILURE); + return RTEXITCODE_SUCCESS; +} + + +/** + * Helper for handleModifyNvramEnrollPlatformKey() and handleModifyNvramEnrollMok(). + * + * This function reads key from file and enrolls it either as a PK (Platform Key) + * or as a MOK (Machine Owner Key). + * + * @returns Exit code. + * @param pszKey Path to a file which contains the key. + * @param pszOwnerUuid Owner's UUID. + * @param nvramStore Reference to the NVRAM store interface. + * @param fPk If True, a key will be enrolled as a PK, otherwise as a MOK. + */ +static RTEXITCODE handleModifyNvramEnrollPlatformKeyOrMok(const char *pszKey, const char *pszOwnerUuid, + ComPtr &nvramStore, bool fPk) +{ + RTFILE hKeyFile; + + int vrc = RTFileOpen(&hKeyFile, pszKey, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE); + if (RT_SUCCESS(vrc)) + { + uint64_t cbSize; + vrc = RTFileQuerySize(hKeyFile, &cbSize); + if (RT_SUCCESS(vrc)) + { + if (cbSize <= _32K) + { + SafeArray aKey((size_t)cbSize); + vrc = RTFileRead(hKeyFile, aKey.raw(), (size_t)cbSize, NULL); + if (RT_SUCCESS(vrc)) + { + RTFileClose(hKeyFile); + + ComPtr uefiVarStore; + CHECK_ERROR2I_RET(nvramStore, COMGETTER(UefiVariableStore)(uefiVarStore.asOutParam()), RTEXITCODE_FAILURE); + if (fPk) + CHECK_ERROR2I_RET(uefiVarStore, EnrollPlatformKey(ComSafeArrayAsInParam(aKey), Bstr(pszOwnerUuid).raw()), RTEXITCODE_FAILURE); + else + CHECK_ERROR2I_RET(uefiVarStore, AddSignatureToMok(ComSafeArrayAsInParam(aKey), Bstr(pszOwnerUuid).raw(), SignatureType_X509), RTEXITCODE_FAILURE); + + return RTEXITCODE_SUCCESS; + } + else + RTMsgError(Nvram::tr("Cannot read contents of file \"%s\": %Rrc"), pszKey, vrc); + } + else + RTMsgError(Nvram::tr("File \"%s\" is bigger than 32KByte"), pszKey); + } + else + RTMsgError(Nvram::tr("Cannot get size of file \"%s\": %Rrc"), pszKey, vrc); + + RTFileClose(hKeyFile); + } + else + RTMsgError(Nvram::tr("Cannot open file \"%s\": %Rrc"), pszKey, vrc); + + return RTEXITCODE_FAILURE; +} + + +/** + * Handles the 'modifynvram myvm enrollpk' sub-command. + * @returns Exit code. + * @param a The handler argument package. + * @param nvramStore Reference to the NVRAM store interface. + */ +static RTEXITCODE handleModifyNvramEnrollPlatformKey(HandlerArg *a, ComPtr &nvramStore) +{ + static const RTGETOPTDEF s_aOptions[] = + { + /* common options */ + { "--platform-key", 'p', RTGETOPT_REQ_STRING }, + { "--owner-uuid", 'f', RTGETOPT_REQ_STRING } + }; + + const char *pszPlatformKey = NULL; + const char *pszOwnerUuid = NULL; + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, a->argc - 2, &a->argv[2], s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + int c; + RTGETOPTUNION ValueUnion; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'p': + pszPlatformKey = ValueUnion.psz; + break; + case 'f': + pszOwnerUuid = ValueUnion.psz; + break; + default: + return errorGetOpt(c, &ValueUnion); + } + } + + if (!pszPlatformKey) + return errorSyntax(Nvram::tr("No platform key file path was given to \"enrollpk\"")); + if (!pszOwnerUuid) + return errorSyntax(Nvram::tr("No owner UUID was given to \"enrollpk\"")); + + return handleModifyNvramEnrollPlatformKeyOrMok(pszPlatformKey, pszOwnerUuid, nvramStore, true /* fPk */); +} + + +/** + * Handles the 'modifynvram myvm enrollmok' sub-command. + * @returns Exit code. + * @param a The handler argument package. + * @param nvramStore Reference to the NVRAM store interface. + */ +static RTEXITCODE handleModifyNvramEnrollMok(HandlerArg *a, ComPtr &nvramStore) +{ + static const RTGETOPTDEF s_aOptions[] = + { + /* common options */ + { "--mok", 'p', RTGETOPT_REQ_STRING }, + { "--owner-uuid", 'f', RTGETOPT_REQ_STRING } + }; + + const char *pszMok = NULL; + const char *pszOwnerUuid = NULL; + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, a->argc - 2, &a->argv[2], s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + int c; + RTGETOPTUNION ValueUnion; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'p': + pszMok = ValueUnion.psz; + break; + case 'f': + pszOwnerUuid = ValueUnion.psz; + break; + default: + return errorGetOpt(c, &ValueUnion); + } + } + + if (!pszMok) + return errorSyntax(Nvram::tr("No machine owner key file path was given to \"enrollpk\"")); + if (!pszOwnerUuid) + return errorSyntax(Nvram::tr("No owner UUID was given to \"enrollpk\"")); + + return handleModifyNvramEnrollPlatformKeyOrMok(pszMok, pszOwnerUuid, nvramStore, false /* fPk */); +} + + +/** + * Handles the 'modifynvram myvm enrollorclpk' sub-command. + * @returns Exit code. + * @param a The handler argument package. + * @param nvram Reference to the NVRAM store interface. + */ +static RTEXITCODE handleModifyNvramEnrollOraclePlatformKey(HandlerArg *a, ComPtr &nvramStore) +{ + RT_NOREF(a); + + ComPtr uefiVarStore; + CHECK_ERROR2I_RET(nvramStore, COMGETTER(UefiVariableStore)(uefiVarStore.asOutParam()), RTEXITCODE_FAILURE); + + CHECK_ERROR2I_RET(uefiVarStore, EnrollOraclePlatformKey(), RTEXITCODE_FAILURE); + return RTEXITCODE_SUCCESS; +} + + +/** + * Handles the 'modifynvram myvm listvars' sub-command. + * @returns Exit code. + * @param a The handler argument package. + * @param nvram Reference to the NVRAM store interface. + */ +static RTEXITCODE handleModifyNvramListUefiVars(HandlerArg *a, ComPtr &nvramStore) +{ + RT_NOREF(a); + + ComPtr uefiVarStore; + CHECK_ERROR2I_RET(nvramStore, COMGETTER(UefiVariableStore)(uefiVarStore.asOutParam()), RTEXITCODE_FAILURE); + + com::SafeArray aNames; + com::SafeArray aOwnerGuids; + CHECK_ERROR2I_RET(uefiVarStore, QueryVariables(ComSafeArrayAsOutParam(aNames), ComSafeArrayAsOutParam(aOwnerGuids)), RTEXITCODE_FAILURE); + for (size_t i = 0; i < aNames.size(); i++) + { + Bstr strName = aNames[i]; + Bstr strOwnerGuid = aOwnerGuids[i]; + + RTPrintf("%-32ls {%ls}\n", strName.raw(), strOwnerGuid.raw()); + } + + return RTEXITCODE_SUCCESS; +} + + +/** + * Handles the 'modifynvram myvm queryvar' sub-command. + * @returns Exit code. + * @param a The handler argument package. + * @param nvram Reference to the NVRAM store interface. + */ +static RTEXITCODE handleModifyNvramQueryUefiVar(HandlerArg *a, ComPtr &nvramStore) +{ + static const RTGETOPTDEF s_aOptions[] = + { + /* common options */ + { "--name", 'n', RTGETOPT_REQ_STRING }, + { "--filename", 'f', RTGETOPT_REQ_STRING } + }; + + const char *pszVarName = NULL; + const char *pszVarDataFilename = NULL; + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, a->argc - 2, &a->argv[2], s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + int c; + RTGETOPTUNION ValueUnion; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'n': + pszVarName = ValueUnion.psz; + break; + case 'f': + pszVarDataFilename = ValueUnion.psz; + break; + default: + return errorGetOpt(c, &ValueUnion); + } + } + + if (!pszVarName) + return errorSyntax(Nvram::tr("No variable name was given to \"queryvar\"")); + + ComPtr uefiVarStore; + CHECK_ERROR2I_RET(nvramStore, COMGETTER(UefiVariableStore)(uefiVarStore.asOutParam()), RTEXITCODE_FAILURE); + + Bstr strOwnerGuid; + com::SafeArray aVarAttrs; + com::SafeArray aData; + CHECK_ERROR2I_RET(uefiVarStore, QueryVariableByName(Bstr(pszVarName).raw(), strOwnerGuid.asOutParam(), + ComSafeArrayAsOutParam(aVarAttrs), ComSafeArrayAsOutParam(aData)), + RTEXITCODE_FAILURE); + + RTEXITCODE rcExit = RTEXITCODE_SUCCESS; + if (!pszVarDataFilename) + { + RTPrintf("%s {%ls}:\n" + "%.*Rhxd\n", pszVarName, strOwnerGuid.raw(), aData.size(), aData.raw()); + } + else + { + /* Just write the data to the file. */ + RTFILE hFile = NIL_RTFILE; + vrc = RTFileOpen(&hFile, pszVarDataFilename, RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE); + if (RT_SUCCESS(vrc)) + { + vrc = RTFileWrite(hFile, aData.raw(), aData.size(), NULL /*pcbWritten*/); + if (RT_FAILURE(vrc)) + rcExit = RTMsgErrorExitFailure(Nvram::tr("Error writing to '%s': %Rrc"), pszVarDataFilename, vrc); + + RTFileClose(hFile); + } + else + rcExit = RTMsgErrorExitFailure(Nvram::tr("Error opening '%s': %Rrc"), pszVarDataFilename, vrc); + } + + return rcExit; +} + + +/** + * Handles the 'modifynvram myvm deletevar' sub-command. + * @returns Exit code. + * @param a The handler argument package. + * @param nvram Reference to the NVRAM store interface. + */ +static RTEXITCODE handleModifyNvramDeleteUefiVar(HandlerArg *a, ComPtr &nvramStore) +{ + static const RTGETOPTDEF s_aOptions[] = + { + /* common options */ + { "--name", 'n', RTGETOPT_REQ_STRING }, + { "--owner-uuid", 'f', RTGETOPT_REQ_STRING } + }; + + const char *pszVarName = NULL; + const char *pszOwnerUuid = NULL; + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, a->argc - 2, &a->argv[2], s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + int c; + RTGETOPTUNION ValueUnion; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'n': + pszVarName = ValueUnion.psz; + break; + case 'f': + pszOwnerUuid = ValueUnion.psz; + break; + default: + return errorGetOpt(c, &ValueUnion); + } + } + + if (!pszVarName) + return errorSyntax(Nvram::tr("No variable name was given to \"deletevar\"")); + if (!pszOwnerUuid) + return errorSyntax(Nvram::tr("No owner UUID was given to \"deletevar\"")); + + ComPtr uefiVarStore; + CHECK_ERROR2I_RET(nvramStore, COMGETTER(UefiVariableStore)(uefiVarStore.asOutParam()), RTEXITCODE_FAILURE); + CHECK_ERROR2I_RET(uefiVarStore, DeleteVariable(Bstr(pszVarName).raw(), Bstr(pszOwnerUuid).raw()), RTEXITCODE_FAILURE); + + return RTEXITCODE_SUCCESS; +} + + +/** + * Handles the 'modifynvram myvm changevar' sub-command. + * @returns Exit code. + * @param a The handler argument package. + * @param nvram Reference to the NVRAM store interface. + */ +static RTEXITCODE handleModifyNvramChangeUefiVar(HandlerArg *a, ComPtr &nvramStore) +{ + static const RTGETOPTDEF s_aOptions[] = + { + /* common options */ + { "--name", 'n', RTGETOPT_REQ_STRING }, + { "--filename", 'f', RTGETOPT_REQ_STRING } + }; + + const char *pszVarName = NULL; + const char *pszVarDataFilename = NULL; + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, a->argc - 2, &a->argv[2], s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + int c; + RTGETOPTUNION ValueUnion; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'n': + pszVarName = ValueUnion.psz; + break; + case 'f': + pszVarDataFilename = ValueUnion.psz; + break; + default: + return errorGetOpt(c, &ValueUnion); + } + } + + if (!pszVarName) + return errorSyntax(Nvram::tr("No variable name was given to \"changevar\"")); + if (!pszVarDataFilename) + return errorSyntax(Nvram::tr("No variable data filename was given to \"changevar\"")); + + RTFILE hFile = NIL_RTFILE; + RTEXITCODE rcExit = RTEXITCODE_SUCCESS; + vrc = RTFileOpen(&hFile, pszVarDataFilename, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE); + if (RT_SUCCESS(vrc)) + { + uint64_t cbFile = 0; + vrc = RTFileQuerySize(hFile, &cbFile); + if (RT_SUCCESS(vrc)) + { + com::SafeArray aData; + aData.resize(cbFile); + + vrc = RTFileRead(hFile, aData.raw(), aData.size(), NULL /*pcbRead*/); + RTFileClose(hFile); + + if (RT_SUCCESS(vrc)) + { + ComPtr uefiVarStore; + CHECK_ERROR2I_RET(nvramStore, COMGETTER(UefiVariableStore)(uefiVarStore.asOutParam()), RTEXITCODE_FAILURE); + CHECK_ERROR2I_RET(uefiVarStore, ChangeVariable(Bstr(pszVarName).raw(), ComSafeArrayAsInParam(aData)), RTEXITCODE_FAILURE); + } + else + rcExit = RTMsgErrorExitFailure(Nvram::tr("Error reading from '%s': %Rrc"), pszVarDataFilename, vrc); + } + } + else + rcExit = RTMsgErrorExitFailure(Nvram::tr("Error opening '%s': %Rrc"), pszVarDataFilename, vrc); + + return rcExit; +} + + +/** + * Handles the 'modifynvram' command. + * @returns Exit code. + * @param a The handler argument package. + */ +RTEXITCODE handleModifyNvram(HandlerArg *a) +{ + HRESULT hrc = S_OK; + ComPtr machine; + ComPtr nvramStore; + + if (a->argc < 2) + return errorNoSubcommand(); + + /* 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_Write), RTEXITCODE_FAILURE); + + /* get the mutable session machine */ + a->session->COMGETTER(Machine)(machine.asOutParam()); + hrc = machine->COMGETTER(NonVolatileStore)(nvramStore.asOutParam()); + if (FAILED(hrc)) goto leave; + + if (!strcmp(a->argv[1], "inituefivarstore")) + { + setCurrentSubcommand(HELP_SCOPE_MODIFYNVRAM_INITUEFIVARSTORE); + hrc = handleModifyNvramInitUefiVarStore(a, nvramStore) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL; + } + else if (!strcmp(a->argv[1], "enrollmssignatures")) + { + setCurrentSubcommand(HELP_SCOPE_MODIFYNVRAM_ENROLLMSSIGNATURES); + hrc = handleModifyNvramEnrollMsSignatures(a, nvramStore) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL; + } + else if (!strcmp(a->argv[1], "enrollpk")) + { + setCurrentSubcommand(HELP_SCOPE_MODIFYNVRAM_ENROLLPK); + hrc = handleModifyNvramEnrollPlatformKey(a, nvramStore) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL; + } + else if (!strcmp(a->argv[1], "enrollmok")) + { + setCurrentSubcommand(HELP_SCOPE_MODIFYNVRAM_ENROLLMOK); + hrc = handleModifyNvramEnrollMok(a, nvramStore) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL; + } + else if (!strcmp(a->argv[1], "enrollorclpk")) + { + setCurrentSubcommand(HELP_SCOPE_MODIFYNVRAM_ENROLLORCLPK); + hrc = handleModifyNvramEnrollOraclePlatformKey(a, nvramStore) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL; + } + else if (!strcmp(a->argv[1], "listvars")) + { + setCurrentSubcommand(HELP_SCOPE_MODIFYNVRAM_LISTVARS); + hrc = handleModifyNvramListUefiVars(a, nvramStore) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL; + } + else if (!strcmp(a->argv[1], "queryvar")) + { + setCurrentSubcommand(HELP_SCOPE_MODIFYNVRAM_QUERYVAR); + hrc = handleModifyNvramQueryUefiVar(a, nvramStore) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL; + } + else if (!strcmp(a->argv[1], "deletevar")) + { + setCurrentSubcommand(HELP_SCOPE_MODIFYNVRAM_DELETEVAR); + hrc = handleModifyNvramDeleteUefiVar(a, nvramStore) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL; + } + else if (!strcmp(a->argv[1], "changevar")) + { + setCurrentSubcommand(HELP_SCOPE_MODIFYNVRAM_CHANGEVAR); + hrc = handleModifyNvramChangeUefiVar(a, nvramStore) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL; + } + else + return errorUnknownSubcommand(a->argv[0]); + + /* commit changes */ + if (SUCCEEDED(hrc)) + CHECK_ERROR(machine, SaveSettings()); + +leave: + /* it's important to always close sessions */ + a->session->UnlockMachine(); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp new file mode 100644 index 00000000..4a360725 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp @@ -0,0 +1,3646 @@ +/* $Id: VBoxManageModifyVM.cpp $ */ +/** @file + * VBoxManage - Implementation of modifyvm command. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "VBoxManage.h" +#include "VBoxManageUtils.h" + +DECLARE_TRANSLATION_CONTEXT(ModifyVM); + +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_VIRT_VMSAVE_VMLOAD, + 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_MDS_CLEAR_ON_SCHED, + MODIFYVM_MDS_CLEAR_ON_VM_ENTRY, + MODIFYVM_NESTED_HW_VIRT, + MODIFYVM_CPUS, + MODIFYVM_CPUHOTPLUG, + MODIFYVM_CPU_PROFILE, + MODIFYVM_PLUGCPU, + MODIFYVM_UNPLUGCPU, + MODIFYVM_SETCPUID, + MODIFYVM_DELCPUID, + MODIFYVM_DELCPUID_OLD, // legacy, different syntax from MODIFYVM_DELCPUID + 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_SYSTEMUUIDLE, + 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, +#ifdef VBOX_WITH_CLOUD_NET + MODIFYVM_CLOUDNET, +#endif /* VBOX_WITH_CLOUD_NET */ + MODIFYVM_HOSTONLYADAPTER, +#ifdef VBOX_WITH_VMNET + MODIFYVM_HOSTONLYNET, +#endif /* VBOX_WITH_VMNET */ + 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_NATLOCALHOSTREACHABLE, + 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_AUDIODRIVER, + MODIFYVM_AUDIOENABLED, + MODIFYVM_AUDIO, /* Deprecated; remove in the next major version. */ + MODIFYVM_AUDIOIN, + MODIFYVM_AUDIOOUT, +#ifdef VBOX_WITH_SHARED_CLIPBOARD + MODIFYVM_CLIPBOARD_MODE, +# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS + MODIFYVM_CLIPBOARD_FILE_TRANSFERS, +# endif +#endif + 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_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, +#if defined(VBOX_WITH_IOMMU_AMD) || defined(VBOX_WITH_IOMMU_INTEL) + MODIFYVM_IOMMU, +#endif +#if defined(VBOX_WITH_TPM) + MODIFYVM_TPM_LOCATION, + MODIFYVM_TPM_TYPE, +#endif + MODIFYVM_DEFAULTFRONTEND, + MODIFYVM_VMPROC_PRIORITY, + MODIFYVM_TESTING_ENABLED, + MODIFYVM_TESTING_MMIO, + MODIFYVM_TESTING_CFG_DWORD, + MODIFYVM_GUEST_DEBUG_PROVIDER, + MODIFYVM_GUEST_DEBUG_IO_PROVIDER, + MODIFYVM_GUEST_DEBUG_ADDRESS, + MODIFYVM_GUEST_DEBUG_PORT, +}; + +static const RTGETOPTDEF g_aModifyVMOptions[] = +{ + OPT1("--name", MODIFYVM_NAME, RTGETOPT_REQ_STRING), + OPT1("--groups", MODIFYVM_GROUPS, RTGETOPT_REQ_STRING), + OPT1("--description", MODIFYVM_DESCRIPTION, RTGETOPT_REQ_STRING), + OPT2("--os-type", "--ostype", MODIFYVM_OSTYPE, RTGETOPT_REQ_STRING), + OPT2("--icon-file", "--iconfile", MODIFYVM_ICONFILE, RTGETOPT_REQ_STRING), + OPT1("--memory", MODIFYVM_MEMORY, RTGETOPT_REQ_UINT32), + OPT2("--page-fusion", "--pagefusion", MODIFYVM_PAGEFUSION, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--vram", MODIFYVM_VRAM, RTGETOPT_REQ_UINT32), + OPT1("--firmware", MODIFYVM_FIRMWARE, RTGETOPT_REQ_STRING), + OPT1("--acpi", MODIFYVM_ACPI, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--ioapic", MODIFYVM_IOAPIC, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--pae", MODIFYVM_PAE, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--long-mode", "--longmode", MODIFYVM_LONGMODE, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--cpuid-portability-level", MODIFYVM_CPUID_PORTABILITY, RTGETOPT_REQ_UINT32), + OPT2("--triple-fault-reset", "--triplefaultreset", MODIFYVM_TFRESET, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--apic", MODIFYVM_APIC, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--x2apic", MODIFYVM_X2APIC, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--paravirt-provider", "--paravirtprovider", MODIFYVM_PARAVIRTPROVIDER, RTGETOPT_REQ_STRING), + OPT2("--paravirt-debug", "--paravirtdebug", MODIFYVM_PARAVIRTDEBUG, RTGETOPT_REQ_STRING), + OPT1("--hwvirtex", MODIFYVM_HWVIRTEX, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--nested-paging", "--nestedpaging", MODIFYVM_NESTEDPAGING, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--large-pages", "--largepages", MODIFYVM_LARGEPAGES, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--vtx-vpid", "--vtxvpid", MODIFYVM_VTXVPID, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--vtx-ux", "--vtxux", MODIFYVM_VTXUX, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--virt-vmsave-vmload", MODIFYVM_VIRT_VMSAVE_VMLOAD, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--ibpb-on-vm-exit", MODIFYVM_IBPB_ON_VM_EXIT, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--ibpb-on-vm-entry", MODIFYVM_IBPB_ON_VM_ENTRY, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--spec-ctrl", MODIFYVM_SPEC_CTRL, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--l1d-flush-on-sched", MODIFYVM_L1D_FLUSH_ON_SCHED, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--l1d-flush-on-vm-entry", MODIFYVM_L1D_FLUSH_ON_VM_ENTRY, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--mds-clear-on-sched", MODIFYVM_MDS_CLEAR_ON_SCHED, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--mds-clear-on-vm-entry", MODIFYVM_MDS_CLEAR_ON_VM_ENTRY, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--nested-hw-virt", MODIFYVM_NESTED_HW_VIRT, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--cpuid-set", "--cpuidset", MODIFYVM_SETCPUID, RTGETOPT_REQ_UINT32_OPTIONAL_PAIR | RTGETOPT_FLAG_HEX), + OPT1("--cpuid-remove", MODIFYVM_DELCPUID, RTGETOPT_REQ_UINT32_OPTIONAL_PAIR | RTGETOPT_FLAG_HEX), + OPT1("--cpuidremove", MODIFYVM_DELCPUID_OLD, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_HEX), /* legacy - syntax differs */ + OPT2("--cpuid-remove-all", "--cpuidremoveall", MODIFYVM_DELALLCPUID, RTGETOPT_REQ_NOTHING), + OPT1("--cpus", MODIFYVM_CPUS, RTGETOPT_REQ_UINT32), + OPT2("--cpu-hotplug", "--cpuhotplug", MODIFYVM_CPUHOTPLUG, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--cpu-profile", MODIFYVM_CPU_PROFILE, RTGETOPT_REQ_STRING), + OPT2("--plug-cpu", "--plugcpu", MODIFYVM_PLUGCPU, RTGETOPT_REQ_UINT32), + OPT2("--unplug-cpu", "--unplugcpu", MODIFYVM_UNPLUGCPU, RTGETOPT_REQ_UINT32), + OPT2("--cpu-execution-cap", "--cpuexecutioncap", MODIFYVM_CPU_EXECTUION_CAP, RTGETOPT_REQ_UINT32), + OPT2("--rtc-use-utc", "--rtcuseutc", MODIFYVM_RTCUSEUTC, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--graphicscontroller", "--graphicscontroller", MODIFYVM_GRAPHICSCONTROLLER, RTGETOPT_REQ_STRING), + OPT2("--monitor-count", "--monitorcount", MODIFYVM_MONITORCOUNT, RTGETOPT_REQ_UINT32), + OPT2("--accelerate-3d", "--accelerate3d", MODIFYVM_ACCELERATE3D, RTGETOPT_REQ_BOOL_ONOFF), +#ifdef VBOX_WITH_VIDEOHWACCEL + OPT2("--accelerate-2d-video", "--accelerate2dvideo", MODIFYVM_ACCELERATE2DVIDEO, RTGETOPT_REQ_BOOL_ONOFF), +#endif + OPT2("--bios-logo-fade-in", "--bioslogofadein", MODIFYVM_BIOSLOGOFADEIN, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--bios-logo-fade-out", "--bioslogofadeout", MODIFYVM_BIOSLOGOFADEOUT, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--bios-logo-display-time", "--bioslogodisplaytime", MODIFYVM_BIOSLOGODISPLAYTIME, RTGETOPT_REQ_UINT32), + OPT2("--bios-logo-image-path", "--bioslogoimagepath", MODIFYVM_BIOSLOGOIMAGEPATH, RTGETOPT_REQ_STRING), + OPT2("--bios-boot-menu", "--biosbootmenu", MODIFYVM_BIOSBOOTMENU, RTGETOPT_REQ_STRING), + OPT2("--bios-system-time-offset", "--biossystemtimeoffset", MODIFYVM_BIOSSYSTEMTIMEOFFSET, RTGETOPT_REQ_INT64), + OPT2("--bios-apic", "--biosapic", MODIFYVM_BIOSAPIC, RTGETOPT_REQ_STRING), + OPT2("--bios-pxe-debug", "--biospxedebug", MODIFYVM_BIOSPXEDEBUG, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--system-uuid-le", "--system-uuid-le", MODIFYVM_SYSTEMUUIDLE, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--boot", MODIFYVM_BOOT, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT1("--hda", MODIFYVM_HDA, RTGETOPT_REQ_STRING), /* deprecated */ + OPT1("--hdb", MODIFYVM_HDB, RTGETOPT_REQ_STRING), /* deprecated */ + OPT1("--hdd", MODIFYVM_HDD, RTGETOPT_REQ_STRING), /* deprecated */ + OPT2("--idec-ontroller", "--idecontroller", MODIFYVM_IDECONTROLLER, RTGETOPT_REQ_STRING), /* deprecated */ + OPT2("--sata-port-count", "--sataportcount", MODIFYVM_SATAPORTCOUNT, RTGETOPT_REQ_UINT32), /* deprecated */ + OPT2("--sata-port", "--sataport", MODIFYVM_SATAPORT, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), /* deprecated */ + OPT1("--sata", MODIFYVM_SATA, RTGETOPT_REQ_STRING), /* deprecated */ + OPT2("--scsi-port", "--scsiport", MODIFYVM_SCSIPORT, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), /* deprecated */ + OPT2("--scsi-type", "--scsitype", MODIFYVM_SCSITYPE, RTGETOPT_REQ_STRING), /* deprecated */ + OPT1("--scsi", MODIFYVM_SCSI, RTGETOPT_REQ_STRING), /* deprecated */ + OPT2("--dvd-pass-through", "--dvdpassthrough", MODIFYVM_DVDPASSTHROUGH, RTGETOPT_REQ_STRING), /* deprecated */ + OPT1("--dvd", MODIFYVM_DVD, RTGETOPT_REQ_STRING), /* deprecated */ + OPT1("--floppy", MODIFYVM_FLOPPY, RTGETOPT_REQ_STRING), /* deprecated */ + OPT2("--nic-trace-file", "--nictracefile", MODIFYVM_NICTRACEFILE, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT2("--nic-trace", "--nictrace", MODIFYVM_NICTRACE, RTGETOPT_REQ_BOOL_ONOFF | RTGETOPT_FLAG_INDEX), + OPT2("--nic-property", "--nicproperty", MODIFYVM_NICPROPERTY, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT2("--nic-type", "--nictype", MODIFYVM_NICTYPE, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT2("--nic-speed", "--nicspeed", MODIFYVM_NICSPEED, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_INDEX), + OPT2("--nic-boot-prio", "--nicbootprio", MODIFYVM_NICBOOTPRIO, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_INDEX), + OPT2("--nic-promisc", "--nicpromisc", MODIFYVM_NICPROMISC, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT2("--nic-bandwidth-group", "--nicbandwidthgroup", MODIFYVM_NICBWGROUP, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT1("--nic", MODIFYVM_NIC, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT2("--cable-connected", "--cableconnected", MODIFYVM_CABLECONNECTED, RTGETOPT_REQ_BOOL_ONOFF | RTGETOPT_FLAG_INDEX), + OPT2("--bridge-adapter", "--bridgeadapter", MODIFYVM_BRIDGEADAPTER, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), +#ifdef VBOX_WITH_CLOUD_NET + OPT2("--cloud-network", "--cloudnetwork", MODIFYVM_CLOUDNET, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), +#endif /* VBOX_WITH_CLOUD_NET */ + OPT2("--host-only-adapter", "--hostonlyadapter", MODIFYVM_HOSTONLYADAPTER, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), +#ifdef VBOX_WITH_VMNET + OPT2("--host-only-net", "--hostonlynet", MODIFYVM_HOSTONLYNET, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), +#endif + OPT1("--intnet", MODIFYVM_INTNET, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT2("--nic-generic-drv", "--nicgenericdrv", MODIFYVM_GENERICDRV, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT2("--nat-network", "--natnetwork", MODIFYVM_NATNETWORKNAME, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT2("--nat-net", "--natnet", MODIFYVM_NATNET, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT2("--nat-bind-ip", "--natbindip", MODIFYVM_NATBINDIP, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT2("--nat-settings", "--natsettings", MODIFYVM_NATSETTINGS, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT2("--nat-pf", "--natpf", MODIFYVM_NATPF, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT2("--nat-alias-mode", "--nataliasmode", MODIFYVM_NATALIASMODE, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT2("--nat-tftp-prefix", "--nattftpprefix", MODIFYVM_NATTFTPPREFIX, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT2("--nat-tftp-file", "--nattftpfile", MODIFYVM_NATTFTPFILE, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT2("--nat-tftp-server", "--nattftpserver", MODIFYVM_NATTFTPSERVER, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT2("--nat-dns-pass-domain", "--natdnspassdomain", MODIFYVM_NATDNSPASSDOMAIN, RTGETOPT_REQ_BOOL_ONOFF | RTGETOPT_FLAG_INDEX), + OPT2("--nat-dns-proxy", "--natdnsproxy", MODIFYVM_NATDNSPROXY, RTGETOPT_REQ_BOOL_ONOFF | RTGETOPT_FLAG_INDEX), + OPT2("--nat-dns-host-resolver", "--natdnshostresolver", MODIFYVM_NATDNSHOSTRESOLVER, RTGETOPT_REQ_BOOL_ONOFF | RTGETOPT_FLAG_INDEX), + OPT2("--nat-localhostreachable", "--natlocalhostreachable", MODIFYVM_NATLOCALHOSTREACHABLE, RTGETOPT_REQ_BOOL_ONOFF | RTGETOPT_FLAG_INDEX), + OPT2("--mac-address", "--macaddress", MODIFYVM_MACADDRESS, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT1("--mouse", MODIFYVM_HIDPTR, RTGETOPT_REQ_STRING), + OPT1("--keyboard", MODIFYVM_HIDKBD, RTGETOPT_REQ_STRING), + OPT2("--uart-mode", "--uartmode", MODIFYVM_UARTMODE, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT2("--uart-type", "--uarttype", MODIFYVM_UARTTYPE, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT1("--uart", MODIFYVM_UART, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), +#if defined(RT_OS_LINUX) || defined(RT_OS_WINDOWS) + OPT2("--lpt-mode", "--lptmode", MODIFYVM_LPTMODE, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), + OPT1("--lpt", MODIFYVM_LPT, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX), +#endif + OPT2("--guest-memory-balloon", "--guestmemoryballoon", MODIFYVM_GUESTMEMORYBALLOON, RTGETOPT_REQ_UINT32), + OPT2("--audio-controller", "--audiocontroller", MODIFYVM_AUDIOCONTROLLER, RTGETOPT_REQ_STRING), + OPT2("--audio-codec", "--audiocodec", MODIFYVM_AUDIOCODEC, RTGETOPT_REQ_STRING), + OPT1("--audio", MODIFYVM_AUDIO, RTGETOPT_REQ_STRING), + OPT2("--audio-driver", "--audiodriver", MODIFYVM_AUDIODRIVER, RTGETOPT_REQ_STRING), + OPT2("--audio-enabled", "--audioenabled", MODIFYVM_AUDIOENABLED, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--audio-in", "--audioin", MODIFYVM_AUDIOIN, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--audio-out", "--audioout", MODIFYVM_AUDIOOUT, RTGETOPT_REQ_BOOL_ONOFF), +#ifdef VBOX_WITH_SHARED_CLIPBOARD + OPT1("--clipboard-mode", MODIFYVM_CLIPBOARD_MODE, RTGETOPT_REQ_STRING), + OPT1("--clipboard", MODIFYVM_CLIPBOARD_MODE, RTGETOPT_REQ_STRING), /* deprecated */ +# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS + OPT1("--clipboard-file-transfers", MODIFYVM_CLIPBOARD_FILE_TRANSFERS, RTGETOPT_REQ_STRING), +# endif +#endif + OPT2("--drag-and-drop", "--draganddrop", MODIFYVM_DRAGANDDROP, RTGETOPT_REQ_STRING), + OPT2("--vrdp-port", "--vrdpport", MODIFYVM_VRDPPORT, RTGETOPT_REQ_STRING), /* deprecated */ + OPT2("--vrdp-address", "--vrdpaddress", MODIFYVM_VRDPADDRESS, RTGETOPT_REQ_STRING), /* deprecated */ + OPT2("--vrdp-auth-type", "--vrdpauthtype", MODIFYVM_VRDPAUTHTYPE, RTGETOPT_REQ_STRING), /* deprecated */ + OPT2("--vrdp-multi-con", "--vrdpmulticon", MODIFYVM_VRDPMULTICON, RTGETOPT_REQ_BOOL_ONOFF), /* deprecated */ + OPT2("--vrdp-reuse-con", "--vrdpreusecon", MODIFYVM_VRDPREUSECON, RTGETOPT_REQ_BOOL_ONOFF), /* deprecated */ + OPT2("--vrdp-video-channel", "--vrdpvideochannel", MODIFYVM_VRDPVIDEOCHANNEL, RTGETOPT_REQ_BOOL_ONOFF), /* deprecated */ + OPT2("--vrdp-video-channel-quality", "--vrdpvideochannelquality",MODIFYVM_VRDPVIDEOCHANNELQUALITY, RTGETOPT_REQ_STRING), /* deprecated */ + OPT1("--vrdp", MODIFYVM_VRDP, RTGETOPT_REQ_BOOL_ONOFF), /* deprecated */ + OPT2("--vrde-property", "--vrdeproperty", MODIFYVM_VRDEPROPERTY, RTGETOPT_REQ_STRING), + OPT2("--vrde-port", "--vrdeport", MODIFYVM_VRDEPORT, RTGETOPT_REQ_STRING), + OPT2("--vrde-address", "--vrdeaddress", MODIFYVM_VRDEADDRESS, RTGETOPT_REQ_STRING), + OPT2("--vrde-auth-type", "--vrdeauthtype", MODIFYVM_VRDEAUTHTYPE, RTGETOPT_REQ_STRING), + OPT2("--vrde-auth-library", "--vrdeauthlibrary", MODIFYVM_VRDEAUTHLIBRARY, RTGETOPT_REQ_STRING), + OPT2("--vrde-multi-con", "--vrdemulticon", MODIFYVM_VRDEMULTICON, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--vrde-reuse-con", "--vrdereusecon", MODIFYVM_VRDEREUSECON, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--vrde-video-channel", "--vrdevideochannel", MODIFYVM_VRDEVIDEOCHANNEL, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--vrde-video-channel-quality", "--vrdevideochannelquality",MODIFYVM_VRDEVIDEOCHANNELQUALITY, RTGETOPT_REQ_STRING), + OPT2("--vrde-extpack", "--vrdeextpack", MODIFYVM_VRDE_EXTPACK, RTGETOPT_REQ_STRING), + OPT1("--vrde", MODIFYVM_VRDE, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--usb-rename", "--usbrename", MODIFYVM_USBRENAME, RTGETOPT_REQ_STRING), + OPT2("--usb-xhci", "--usbxhci", MODIFYVM_USBXHCI, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--usb-ehci", "--usbehci", MODIFYVM_USBEHCI, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--usb-ohci", "--usbohci", MODIFYVM_USBOHCI, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--usb", MODIFYVM_USBOHCI, RTGETOPT_REQ_BOOL_ONOFF), /* deprecated */ + OPT2("--snapshot-folder", "--snapshotfolder", MODIFYVM_SNAPSHOTFOLDER, RTGETOPT_REQ_STRING), + OPT1("--teleporter", MODIFYVM_TELEPORTER_ENABLED, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--teleporter-enabled", "--teleporterenabled", MODIFYVM_TELEPORTER_ENABLED, RTGETOPT_REQ_BOOL_ONOFF), /* deprecated */ + OPT2("--teleporter-port", "--teleporterport", MODIFYVM_TELEPORTER_PORT, RTGETOPT_REQ_UINT32), + OPT2("--teleporter-address", "--teleporteraddress", MODIFYVM_TELEPORTER_ADDRESS, RTGETOPT_REQ_STRING), + OPT2("--teleporter-password", "--teleporterpassword", MODIFYVM_TELEPORTER_PASSWORD, RTGETOPT_REQ_STRING), + OPT2("--teleporter-password-file", "--teleporterpasswordfile", MODIFYVM_TELEPORTER_PASSWORD_FILE, RTGETOPT_REQ_STRING), + OPT1("--tracing-enabled", MODIFYVM_TRACING_ENABLED, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--tracing-config", MODIFYVM_TRACING_CONFIG, RTGETOPT_REQ_STRING), + OPT1("--tracing-allow-vm-access", MODIFYVM_TRACING_ALLOW_VM_ACCESS, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--hardware-uuid", "--hardwareuuid", MODIFYVM_HARDWARE_UUID, RTGETOPT_REQ_STRING), + OPT1("--hpet", MODIFYVM_HPET, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--iocache", MODIFYVM_IOCACHE, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--iocache-size", "--iocachesize", MODIFYVM_IOCACHESIZE, RTGETOPT_REQ_UINT32), + OPT1("--chipset", MODIFYVM_CHIPSET, RTGETOPT_REQ_STRING), +#if defined(VBOX_WITH_IOMMU_AMD) || defined(VBOX_WITH_IOMMU_INTEL) + OPT1("--iommu", MODIFYVM_IOMMU, RTGETOPT_REQ_STRING), +#endif +#if defined(VBOX_WITH_TPM) + OPT1("--tpm-type", MODIFYVM_TPM_TYPE, RTGETOPT_REQ_STRING), + OPT1("--tpm-location", MODIFYVM_TPM_LOCATION, RTGETOPT_REQ_STRING), +#endif +#ifdef VBOX_WITH_RECORDING + OPT1("--recording", MODIFYVM_RECORDING, RTGETOPT_REQ_BOOL_ONOFF), + OPT2("--recording-screens", "--recordingscreens", MODIFYVM_RECORDING_SCREENS, RTGETOPT_REQ_STRING), + OPT2("--recording-file", "--recordingfile", MODIFYVM_RECORDING_FILENAME, RTGETOPT_REQ_STRING), + OPT2("--recording-max-time", "--recordingmaxtime", MODIFYVM_RECORDING_MAXTIME, RTGETOPT_REQ_INT32), + OPT2("--recording-max-size", "--recordingmaxsize", MODIFYVM_RECORDING_MAXSIZE, RTGETOPT_REQ_INT32), + OPT2("--recording-opts", "--recordingopts", MODIFYVM_RECORDING_OPTIONS, RTGETOPT_REQ_STRING), + OPT2("--recording-options", "--recordingoptions", MODIFYVM_RECORDING_OPTIONS, RTGETOPT_REQ_STRING), + OPT2("--recording-video-res", "--recordingvideores", MODIFYVM_RECORDING_VIDEO_RES, RTGETOPT_REQ_STRING), + OPT2("--recording-video-resolution", "--recordingvideoresolution",MODIFYVM_RECORDING_VIDEO_RES, RTGETOPT_REQ_STRING), + OPT2("--recording-video-rate", "--recordingvideorate", MODIFYVM_RECORDING_VIDEO_RATE, RTGETOPT_REQ_UINT32), + OPT2("--recording-video-fps", "--recordingvideofps", MODIFYVM_RECORDING_VIDEO_FPS, RTGETOPT_REQ_UINT32), +#endif + OPT1("--autostart-enabled", MODIFYVM_AUTOSTART_ENABLED, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--autostart-delay", MODIFYVM_AUTOSTART_DELAY, RTGETOPT_REQ_UINT32), + OPT1("--autostop-type", MODIFYVM_AUTOSTOP_TYPE, RTGETOPT_REQ_STRING), +#ifdef VBOX_WITH_PCI_PASSTHROUGH + OPT2("--pci-attach", "--pciattach", MODIFYVM_ATTACH_PCI, RTGETOPT_REQ_STRING), + OPT2("--pci-detach", "--pcidetach", MODIFYVM_DETACH_PCI, RTGETOPT_REQ_STRING), +#endif +#ifdef VBOX_WITH_USB_CARDREADER + OPT2("--usb-card-reader", "--usbcardreader", MODIFYVM_USBCARDREADER, RTGETOPT_REQ_BOOL_ONOFF), +#endif + OPT2("--default-frontend", "--defaultfrontend", MODIFYVM_DEFAULTFRONTEND, RTGETOPT_REQ_STRING), + OPT1("--vm-process-priority", MODIFYVM_VMPROC_PRIORITY, RTGETOPT_REQ_STRING), + OPT1("--testing-enabled", MODIFYVM_TESTING_ENABLED, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--testing-mmio", MODIFYVM_TESTING_MMIO, RTGETOPT_REQ_BOOL_ONOFF), + OPT1("--testing-cfg-dword", MODIFYVM_TESTING_CFG_DWORD, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_INDEX), + OPT1("--guest-debug-provider", MODIFYVM_GUEST_DEBUG_PROVIDER, RTGETOPT_REQ_STRING), + OPT1("--guest-debug-io-provider", MODIFYVM_GUEST_DEBUG_IO_PROVIDER, RTGETOPT_REQ_STRING), + OPT1("--guest-debug-address", MODIFYVM_GUEST_DEBUG_ADDRESS, RTGETOPT_REQ_STRING), + OPT1("--guest-debug-port", MODIFYVM_GUEST_DEBUG_PORT, RTGETOPT_REQ_UINT32), +}; + +static void vrdeWarningDeprecatedOption(const char *pszOption) +{ + RTStrmPrintf(g_pStdErr, ModifyVM::tr("Warning: '--vrdp%s' is deprecated. Use '--vrde%s'.\n"), pszOption, pszOption); +} + + +/** + * Wrapper around IMachine::SetExtraData that does the error reporting. + * + * @returns COM result code. + * @param rSessionMachine The IMachine. + * @param pszVariable The variable to set. + * @param pszValue The value to set. To delete pass empty string, not + * NULL. + */ +static HRESULT setExtraData(ComPtr &rSessionMachine, const char *pszVariable, const char *pszValue) +{ + HRESULT hrc = rSessionMachine->SetExtraData(Bstr(pszVariable).raw(), Bstr(pszValue).raw()); + if (FAILED(hrc)) + { + char *pszContext = RTStrAPrintf2("IMachine::SetExtraData('%s', '%s')", pszVariable, pszValue); + com::GlueHandleComError(rSessionMachine, pszContext, hrc, __FILE__, __LINE__); + RTStrFree(pszContext); + } + return hrc; +} + + +#ifdef VBOX_WITH_PCI_PASSTHROUGH +/** Parse PCI address in format 01:02.03 and convert it to the numeric representation. */ +static int32_t parsePci(const char* szPciAddr) +{ + uint8_t aVals[3] = {0, 0, 0}; + + char *pszNext; + int vrc = RTStrToUInt8Ex(pszNext, &pszNext, 16, &aVals[0]); + if (RT_FAILURE(vrc) || pszNext == NULL || *pszNext != ':') + return -1; + + vrc = RTStrToUInt8Ex(pszNext+1, &pszNext, 16, &aVals[1]); + if (RT_FAILURE(vrc) || pszNext == NULL || *pszNext != '.') + return -1; + + vrc = RTStrToUInt8Ex(pszNext+1, &pszNext, 16, &aVals[2]); + if (RT_FAILURE(vrc) || pszNext == NULL) + return -1; + + return (aVals[0] << 8) | (aVals[1] << 3) | (aVals[2] << 0); +} +#endif + +void parseGroups(const char *pcszGroups, com::SafeArray *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 +int parseScreens(const char *pcszScreens, com::SafeArray *pScreens) +{ + if (!RTStrICmp(pcszScreens, "all")) + { + for (uint32_t i = 0; i < pScreens->size(); i++) + (*pScreens)[i] = TRUE; + return VINF_SUCCESS; + } + if (!RTStrICmp(pcszScreens, "none")) + { + for (uint32_t i = 0; i < pScreens->size(); i++) + (*pScreens)[i] = FALSE; + return VINF_SUCCESS; + } + while (pcszScreens && *pcszScreens) + { + char *pszNext; + uint32_t iScreen; + int vrc = RTStrToUInt32Ex(pcszScreens, &pszNext, 0, &iScreen); + if (RT_FAILURE(vrc)) + return VERR_PARSE_ERROR; + if (iScreen >= pScreens->size()) + return VERR_PARSE_ERROR; + if (pszNext && *pszNext) + { + pszNext = RTStrStripL(pszNext); + if (*pszNext != ',') + return VERR_PARSE_ERROR; + pszNext++; + } + (*pScreens)[iScreen] = true; + pcszScreens = pszNext; + } + return VINF_SUCCESS; +} +#endif + +static int parseNum(uint32_t uIndex, unsigned cMaxIndex, const char *pszName) +{ + if ( uIndex >= 1 + && uIndex <= cMaxIndex) + return uIndex; + errorArgument(ModifyVM::tr("Invalid %s number %u"), pszName, uIndex); + return 0; +} + +VMProcPriority_T nameToVMProcPriority(const char *pszName) +{ + if (!RTStrICmp(pszName, "default")) + return VMProcPriority_Default; + if (!RTStrICmp(pszName, "flat")) + return VMProcPriority_Flat; + if (!RTStrICmp(pszName, "low")) + return VMProcPriority_Low; + if (!RTStrICmp(pszName, "normal")) + return VMProcPriority_Normal; + if (!RTStrICmp(pszName, "high")) + return VMProcPriority_High; + + return VMProcPriority_Invalid; +} + +RTEXITCODE handleModifyVM(HandlerArg *a) +{ + int c; + HRESULT hrc; + Bstr name; + + /* VM ID + at least one parameter. Parameter arguments are checked + * individually. */ + if (a->argc < 2) + return errorSyntax(ModifyVM::tr("Not enough parameters")); + + /* try to find the given sessionMachine */ + ComPtr 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 sessionMachine; + CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE); + + ComPtr biosSettings; + sessionMachine->COMGETTER(BIOSSettings)(biosSettings.asOutParam()); + + ComPtr pGraphicsAdapter; + sessionMachine->COMGETTER(GraphicsAdapter)(pGraphicsAdapter.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 (hrc) + && (c = RTGetOpt(&GetOptState, &ValueUnion))) + { + switch (c) + { + case MODIFYVM_NAME: + { + CHECK_ERROR(sessionMachine, COMSETTER(Name)(Bstr(ValueUnion.psz).raw())); + break; + } + case MODIFYVM_GROUPS: + { + com::SafeArray 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(ModifyVM::tr("Cannot open file \"%s\": %Rrc"), ValueUnion.psz, vrc); + hrc = E_FAIL; + break; + } + uint64_t cbSize; + vrc = RTFileQuerySize(iconFile, &cbSize); + if (RT_FAILURE(vrc)) + { + RTMsgError(ModifyVM::tr("Cannot get size of file \"%s\": %Rrc"), ValueUnion.psz, vrc); + hrc = E_FAIL; + break; + } + if (cbSize > _256K) + { + RTMsgError(ModifyVM::tr("File \"%s\" is bigger than 256KByte"), ValueUnion.psz); + hrc = E_FAIL; + break; + } + SafeArray icon((size_t)cbSize); + hrc = RTFileRead(iconFile, icon.raw(), (size_t)cbSize, NULL); + if (RT_FAILURE(vrc)) + { + RTMsgError(ModifyVM::tr("Cannot read contents of file \"%s\": %Rrc"), ValueUnion.psz, vrc); + hrc = 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(pGraphicsAdapter, 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(ModifyVM::tr("Invalid --firmware argument '%s'"), ValueUnion.psz); + hrc = 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(ModifyVM::tr("Invalid --paravirtprovider argument '%s'"), ValueUnion.psz); + hrc = 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: + { + 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(ModifyVM::tr("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_VIRT_VMSAVE_VMLOAD: + CHECK_ERROR(sessionMachine, SetHWVirtExProperty(HWVirtExPropertyType_VirtVmsaveVmload, 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_MDS_CLEAR_ON_SCHED: + CHECK_ERROR(sessionMachine, SetCPUProperty(CPUPropertyType_MDSClearOnEMTScheduling, ValueUnion.f)); + break; + + case MODIFYVM_MDS_CLEAR_ON_VM_ENTRY: + CHECK_ERROR(sessionMachine, SetCPUProperty(CPUPropertyType_MDSClearOnVMEntry, 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(pGraphicsAdapter, COMSETTER(GraphicsControllerType)(GraphicsControllerType_Null)); + else if ( !RTStrICmp(ValueUnion.psz, "vboxvga") + || !RTStrICmp(ValueUnion.psz, "vbox") + || !RTStrICmp(ValueUnion.psz, "vga") + || !RTStrICmp(ValueUnion.psz, "vesa")) + CHECK_ERROR(pGraphicsAdapter, COMSETTER(GraphicsControllerType)(GraphicsControllerType_VBoxVGA)); +#ifdef VBOX_WITH_VMSVGA + else if ( !RTStrICmp(ValueUnion.psz, "vmsvga") + || !RTStrICmp(ValueUnion.psz, "vmware")) + CHECK_ERROR(pGraphicsAdapter, COMSETTER(GraphicsControllerType)(GraphicsControllerType_VMSVGA)); + else if ( !RTStrICmp(ValueUnion.psz, "vboxsvga") + || !RTStrICmp(ValueUnion.psz, "svga")) + CHECK_ERROR(pGraphicsAdapter, COMSETTER(GraphicsControllerType)(GraphicsControllerType_VBoxSVGA)); +#endif + else + { + errorArgument(ModifyVM::tr("Invalid --graphicscontroller argument '%s'"), ValueUnion.psz); + hrc = E_FAIL; + } + break; + } + + case MODIFYVM_MONITORCOUNT: + { + CHECK_ERROR(pGraphicsAdapter, COMSETTER(MonitorCount)(ValueUnion.u32)); + break; + } + + case MODIFYVM_ACCELERATE3D: + { + CHECK_ERROR(pGraphicsAdapter, COMSETTER(Accelerate3DEnabled)(ValueUnion.f)); + break; + } + +#ifdef VBOX_WITH_VIDEOHWACCEL + case MODIFYVM_ACCELERATE2DVIDEO: + { + CHECK_ERROR(pGraphicsAdapter, 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(ModifyVM::tr("Invalid --biosbootmenu argument '%s'"), ValueUnion.psz); + hrc = 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(ModifyVM::tr("Invalid --biosapic argument '%s'"), ValueUnion.psz); + hrc = 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_SYSTEMUUIDLE: + { + CHECK_ERROR(biosSettings, COMSETTER(SMBIOSUuidLittleEndian)(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(ModifyVM::tr("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 hardDisk; + hrc = openMedium(a, ValueUnion.psz, DeviceType_HardDisk, + AccessMode_ReadWrite, hardDisk, + false /* fForceNewUuidOnOpen */, + false /* fSilent */); + if (FAILED(hrc)) + break; + if (hardDisk) + { + CHECK_ERROR(sessionMachine, AttachDevice(bstrController.raw(), + u1, u2, + DeviceType_HardDisk, + hardDisk)); + } + else + hrc = E_FAIL; + } + break; + } + + case MODIFYVM_IDECONTROLLER: // deprecated + { + ComPtr 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(ModifyVM::tr("Invalid --idecontroller argument '%s'"), ValueUnion.psz); + hrc = E_FAIL; + } + break; + } + + case MODIFYVM_SATAPORTCOUNT: // deprecated + { + ComPtr SataCtl; + CHECK_ERROR(sessionMachine, GetStorageControllerByName(Bstr("SATA").raw(), + SataCtl.asOutParam())); + + if (SUCCEEDED(hrc) && 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 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(ModifyVM::tr("Invalid --usb argument '%s'"), ValueUnion.psz); + break; + } + + case MODIFYVM_SCSIPORT: // deprecated + { + if (!RTStrICmp(ValueUnion.psz, "none")) + { + hrc = sessionMachine->DetachDevice(Bstr("LsiLogic").raw(), + GetOptState.uIndex, 0); + if (FAILED(hrc)) + CHECK_ERROR(sessionMachine, DetachDevice(Bstr("BusLogic").raw(), + GetOptState.uIndex, 0)); + } + else + { + ComPtr hardDisk; + hrc = openMedium(a, ValueUnion.psz, DeviceType_HardDisk, + AccessMode_ReadWrite, hardDisk, + false /* fForceNewUuidOnOpen */, + false /* fSilent */); + if (FAILED(hrc)) + break; + if (hardDisk) + { + hrc = sessionMachine->AttachDevice(Bstr("LsiLogic").raw(), + GetOptState.uIndex, 0, + DeviceType_HardDisk, + hardDisk); + if (FAILED(hrc)) + CHECK_ERROR(sessionMachine, + AttachDevice(Bstr("BusLogic").raw(), + GetOptState.uIndex, 0, + DeviceType_HardDisk, + hardDisk)); + } + else + hrc = E_FAIL; + } + break; + } + + case MODIFYVM_SCSITYPE: // deprecated + { + ComPtr ctl; + + if (!RTStrICmp(ValueUnion.psz, "LsiLogic")) + { + hrc = sessionMachine->RemoveStorageController(Bstr("BusLogic").raw()); + if (FAILED(hrc)) + CHECK_ERROR(sessionMachine, RemoveStorageController(Bstr("LsiLogic").raw())); + + CHECK_ERROR(sessionMachine, + AddStorageController(Bstr("LsiLogic").raw(), + StorageBus_SCSI, + ctl.asOutParam())); + + if (SUCCEEDED(hrc)) + CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_LsiLogic)); + } + else if (!RTStrICmp(ValueUnion.psz, "BusLogic")) + { + hrc = sessionMachine->RemoveStorageController(Bstr("LsiLogic").raw()); + if (FAILED(hrc)) + CHECK_ERROR(sessionMachine, RemoveStorageController(Bstr("BusLogic").raw())); + + CHECK_ERROR(sessionMachine, + AddStorageController(Bstr("BusLogic").raw(), + StorageBus_SCSI, + ctl.asOutParam())); + + if (SUCCEEDED(hrc)) + CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_BusLogic)); + } + else + return errorArgument(ModifyVM::tr("Invalid --scsitype argument '%s'"), ValueUnion.psz); + break; + } + + case MODIFYVM_SCSI: // deprecated + { + if (!RTStrICmp(ValueUnion.psz, "on") || !RTStrICmp(ValueUnion.psz, "enable")) + { + ComPtr ctl; + + CHECK_ERROR(sessionMachine, AddStorageController(Bstr("BusLogic").raw(), + StorageBus_SCSI, + ctl.asOutParam())); + if (SUCCEEDED(hrc)) + CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_BusLogic)); + } + else if (!RTStrICmp(ValueUnion.psz, "off") || !RTStrICmp(ValueUnion.psz, "disable")) + { + hrc = sessionMachine->RemoveStorageController(Bstr("BusLogic").raw()); + if (FAILED(hrc)) + 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 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 host; + CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam())); + hrc = 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(ModifyVM::tr("Invalid host DVD drive name \"%s\""), ValueUnion.psz + 5); + hrc = E_FAIL; + break; + } + hrc = host->FindHostDVDDrive(Bstr(szPathReal).raw(), + dvdMedium.asOutParam()); + if (!dvdMedium) + { + errorArgument(ModifyVM::tr("Invalid host DVD drive name \"%s\""), ValueUnion.psz + 5); + hrc = E_FAIL; + break; + } + } + } + else + { + hrc = openMedium(a, ValueUnion.psz, DeviceType_DVD, + AccessMode_ReadOnly, dvdMedium, + false /* fForceNewUuidOnOpen */, + false /* fSilent */); + if (FAILED(hrc)) + break; + if (!dvdMedium) + { + hrc = E_FAIL; + break; + } + } + + CHECK_ERROR(sessionMachine, MountMedium(Bstr("IDE Controller").raw(), + 1, 0, + dvdMedium, + FALSE /* aForce */)); + break; + } + + case MODIFYVM_FLOPPY: // deprecated + { + ComPtr floppyMedium; + ComPtr 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 host; + CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam())); + hrc = host->FindHostFloppyDrive(Bstr(ValueUnion.psz + 5).raw(), + floppyMedium.asOutParam()); + if (!floppyMedium) + { + errorArgument(ModifyVM::tr("Invalid host floppy drive name \"%s\""), ValueUnion.psz + 5); + hrc = E_FAIL; + break; + } + } + else + { + hrc = openMedium(a, ValueUnion.psz, DeviceType_Floppy, + AccessMode_ReadWrite, floppyMedium, + false /* fForceNewUuidOnOpen */, + false /* fSilent */); + if (FAILED(hrc)) + break; + if (!floppyMedium) + { + hrc = 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 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 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 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(ModifyVM::tr("Invalid --nicproperty%d argument '%s'"), GetOptState.uIndex, ValueUnion.psz); + hrc = E_FAIL; + } + RTStrFree(pszProperty); + } + else + { + RTStrmPrintf(g_pStdErr, ModifyVM::tr("Error: Failed to allocate memory for --nicproperty%d '%s'\n"), + GetOptState.uIndex, ValueUnion.psz); + hrc = E_FAIL; + } + } + break; + } + case MODIFYVM_NICTYPE: + { + if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC")) + break; + + ComPtr 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)); + } + else if (!RTStrICmp(ValueUnion.psz, "Am79C960")) + { + CHECK_ERROR(nic, COMSETTER(AdapterType)(NetworkAdapterType_Am79C960)); + } +#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 if (!RTStrICmp(ValueUnion.psz, "NE1000")) + { + CHECK_ERROR(nic, COMSETTER(AdapterType)(NetworkAdapterType_NE1000)); + } + else if (!RTStrICmp(ValueUnion.psz, "NE2000")) + { + CHECK_ERROR(nic, COMSETTER(AdapterType)(NetworkAdapterType_NE2000)); + } + else if (!RTStrICmp(ValueUnion.psz, "WD8003")) + { + CHECK_ERROR(nic, COMSETTER(AdapterType)(NetworkAdapterType_WD8003)); + } + else if (!RTStrICmp(ValueUnion.psz, "WD8013")) + { + CHECK_ERROR(nic, COMSETTER(AdapterType)(NetworkAdapterType_WD8013)); + } + else if (!RTStrICmp(ValueUnion.psz, "3C503")) + { + CHECK_ERROR(nic, COMSETTER(AdapterType)(NetworkAdapterType_ELNK2)); + } + else if (!RTStrICmp(ValueUnion.psz, "3C501")) + { + CHECK_ERROR(nic, COMSETTER(AdapterType)(NetworkAdapterType_ELNK1)); + } + else + { + errorArgument(ModifyVM::tr("Invalid NIC type '%s' specified for NIC %u"), + ValueUnion.psz, GetOptState.uIndex); + hrc = E_FAIL; + } + break; + } + + case MODIFYVM_NICSPEED: + { + if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC")) + break; + + ComPtr 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 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(ModifyVM::tr("Invalid boot priority '%u' specfied for NIC %u"), ValueUnion.u32, GetOptState.uIndex); + hrc = 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(ModifyVM::tr("Unknown promiscuous mode policy '%s'"), ValueUnion.psz); + hrc = E_INVALIDARG; + break; + } + + if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC")) + break; + + ComPtr 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 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 bwCtrl; + ComPtr bwGroup; + + CHECK_ERROR(sessionMachine, COMGETTER(BandwidthControl)(bwCtrl.asOutParam())); + + if (SUCCEEDED(hrc)) + { + CHECK_ERROR(bwCtrl, GetBandwidthGroup(Bstr(ValueUnion.psz).raw(), bwGroup.asOutParam())); + if (SUCCEEDED(hrc)) + { + CHECK_ERROR(nic, COMSETTER(BandwidthGroup)(bwGroup)); + } + } + } + break; + } + + case MODIFYVM_NIC: + { + if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC")) + break; + + ComPtr nic; + CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam())); + ASSERT(nic); + + /* + * Check if the NIC is already enabled. Do not try to + * enable it if it already is. That makes a + * difference for saved VMs for which you can change + * the NIC attachment, but can't change the NIC + * enabled status (yes, the setter also should not + * freak out about a no-op request). + */ + BOOL fEnabled;; + CHECK_ERROR(nic, COMGETTER(Enabled)(&fEnabled)); + + if (!RTStrICmp(ValueUnion.psz, "none")) + { + if (RT_BOOL(fEnabled)) + CHECK_ERROR(nic, COMSETTER(Enabled)(FALSE)); + } + else if (!RTStrICmp(ValueUnion.psz, "null")) + { + if (!fEnabled) + CHECK_ERROR(nic, COMSETTER(Enabled)(TRUE)); + CHECK_ERROR(nic, COMSETTER(AttachmentType)(NetworkAttachmentType_Null)); + } + else if (!RTStrICmp(ValueUnion.psz, "nat")) + { + if (!fEnabled) + 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 */ + { + if (!fEnabled) + CHECK_ERROR(nic, COMSETTER(Enabled)(TRUE)); + CHECK_ERROR(nic, COMSETTER(AttachmentType)(NetworkAttachmentType_Bridged)); + } + else if (!RTStrICmp(ValueUnion.psz, "intnet")) + { + if (!fEnabled) + CHECK_ERROR(nic, COMSETTER(Enabled)(TRUE)); + CHECK_ERROR(nic, COMSETTER(AttachmentType)(NetworkAttachmentType_Internal)); + } + else if (!RTStrICmp(ValueUnion.psz, "hostonly")) + { + if (!fEnabled) + CHECK_ERROR(nic, COMSETTER(Enabled)(TRUE)); + CHECK_ERROR(nic, COMSETTER(AttachmentType)(NetworkAttachmentType_HostOnly)); + } +#ifdef VBOX_WITH_VMNET + else if (!RTStrICmp(ValueUnion.psz, "hostonlynet")) + { + if (!fEnabled) + CHECK_ERROR(nic, COMSETTER(Enabled)(TRUE)); + CHECK_ERROR(nic, COMSETTER(AttachmentType)(NetworkAttachmentType_HostOnlyNetwork)); + } +#endif /* VBOX_WITH_VMNET */ + else if (!RTStrICmp(ValueUnion.psz, "generic")) + { + if (!fEnabled) + CHECK_ERROR(nic, COMSETTER(Enabled)(TRUE)); + CHECK_ERROR(nic, COMSETTER(AttachmentType)(NetworkAttachmentType_Generic)); + } + else if (!RTStrICmp(ValueUnion.psz, "natnetwork")) + { + if (!fEnabled) + CHECK_ERROR(nic, COMSETTER(Enabled)(TRUE)); + CHECK_ERROR(nic, COMSETTER(AttachmentType)(NetworkAttachmentType_NATNetwork)); + } +#ifdef VBOX_WITH_CLOUD_NET + else if (!RTStrICmp(ValueUnion.psz, "cloud")) + { + if (!fEnabled) + CHECK_ERROR(nic, COMSETTER(Enabled)(TRUE)); + CHECK_ERROR(nic, COMSETTER(AttachmentType)(NetworkAttachmentType_Cloud)); + } +#endif /* VBOX_WITH_CLOUD_NET */ + else + { + errorArgument(ModifyVM::tr("Invalid type '%s' specfied for NIC %u"), ValueUnion.psz, GetOptState.uIndex); + hrc = E_FAIL; + } + break; + } + + case MODIFYVM_CABLECONNECTED: + { + if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC")) + break; + + ComPtr 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 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())); + verifyHostNetworkInterfaceName(a->virtualBox, ValueUnion.psz, + HostNetworkInterfaceType_Bridged); + } + break; + } + +#ifdef VBOX_WITH_CLOUD_NET + case MODIFYVM_CLOUDNET: + { + if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC")) + break; + + ComPtr nic; + CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam())); + ASSERT(nic); + + /* remove it? */ + if (!RTStrICmp(ValueUnion.psz, "none")) + { + CHECK_ERROR(nic, COMSETTER(CloudNetwork)(Bstr().raw())); + } + else + { + CHECK_ERROR(nic, COMSETTER(CloudNetwork)(Bstr(ValueUnion.psz).raw())); + } + break; + } +#endif /* VBOX_WITH_CLOUD_NET */ + + case MODIFYVM_HOSTONLYADAPTER: + { + if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC")) + break; + + ComPtr 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())); + verifyHostNetworkInterfaceName(a->virtualBox, ValueUnion.psz, + HostNetworkInterfaceType_HostOnly); + } + break; + } + +#ifdef VBOX_WITH_VMNET + case MODIFYVM_HOSTONLYNET: + { + if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC")) + break; + + ComPtr nic; + CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam())); + ASSERT(nic); + + /* remove it? */ + if (!RTStrICmp(ValueUnion.psz, "none")) + { + CHECK_ERROR(nic, COMSETTER(HostOnlyNetwork)(Bstr().raw())); + } + else + { + CHECK_ERROR(nic, COMSETTER(HostOnlyNetwork)(Bstr(ValueUnion.psz).raw())); + } + break; + } +#endif /* VBOX_WITH_VMNET */ + + case MODIFYVM_INTNET: + { + if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC")) + break; + + ComPtr 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 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 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 nic; + CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam())); + ASSERT(nic); + + ComPtr 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 nic; + CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam())); + ASSERT(nic); + + ComPtr 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(ModifyVM::tr("Missing or invalid argument to '%s'"), \ + GetOptState.pDef->pszLong); \ + } \ + ch++; \ + } \ + *ch = '\0'; \ + ch++; \ + } while(0) + + case MODIFYVM_NATSETTINGS: + { + ComPtr nic; + ComPtr 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 nic; + CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam())); + ASSERT(nic); + + ComPtr 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(ModifyVM::tr("Invalid proto '%s' specfied for NIC %u"), ValueUnion.psz, GetOptState.uIndex); + hrc = 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(ModifyVM::tr("Not enough parameters")); + CHECK_ERROR(engine, RemoveRedirect(Bstr(ValueUnion.psz).raw())); + } + break; + } + #undef ITERATE_TO_NEXT_TERM + case MODIFYVM_NATALIASMODE: + { + ComPtr nic; + ComPtr 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 nic; + CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam())); + ASSERT(nic); + + ComPtr 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 nic; + CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam())); + ASSERT(nic); + + ComPtr 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 nic; + CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam())); + ASSERT(nic); + + ComPtr 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 nic; + CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam())); + ASSERT(nic); + + ComPtr 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 nic; + CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam())); + ASSERT(nic); + + ComPtr 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 nic; + CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam())); + ASSERT(nic); + + ComPtr engine; + CHECK_ERROR(nic, COMGETTER(NATEngine)(engine.asOutParam())); + + CHECK_ERROR(engine, COMSETTER(DNSUseHostResolver)(ValueUnion.f)); + break; + } + + case MODIFYVM_NATLOCALHOSTREACHABLE: + { + if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC")) + break; + + ComPtr nic; + CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam())); + ASSERT(nic); + + ComPtr engine; + CHECK_ERROR(nic, COMGETTER(NATEngine)(engine.asOutParam())); + + CHECK_ERROR(engine, COMSETTER(LocalhostReachable)(ValueUnion.f)); + break; + } + + case MODIFYVM_MACADDRESS: + { + if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC")) + break; + + ComPtr 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(hrc)) + fEnableUsb = true; + } + else if (!RTStrICmp(ValueUnion.psz, "usbtablet")) + { + CHECK_ERROR(sessionMachine, COMSETTER(PointingHIDType)(PointingHIDType_USBTablet)); + if (SUCCEEDED(hrc)) + fEnableUsb = true; + } + else if (!RTStrICmp(ValueUnion.psz, "usbmultitouch")) + { + CHECK_ERROR(sessionMachine, COMSETTER(PointingHIDType)(PointingHIDType_USBMultiTouch)); + if (SUCCEEDED(hrc)) + fEnableUsb = true; + } + else if (!RTStrICmp(ValueUnion.psz, "usbmtscreenpluspad")) + { + CHECK_ERROR(sessionMachine, COMSETTER(PointingHIDType)(PointingHIDType_USBMultiTouchScreenPlusPad)); + if (SUCCEEDED(hrc)) + fEnableUsb = true; + } + else if (!RTStrICmp(ValueUnion.psz, "none")) + { + CHECK_ERROR(sessionMachine, COMSETTER(PointingHIDType)(PointingHIDType_None)); + } + else + { + errorArgument(ModifyVM::tr("Invalid type '%s' specfied for pointing device"), ValueUnion.psz); + hrc = E_FAIL; + } + if (fEnableUsb) + { + /* Make sure either the OHCI or xHCI controller is enabled. */ + ULONG cOhciCtrls = 0; + ULONG cXhciCtrls = 0; + hrc = sessionMachine->GetUSBControllerCountByType(USBControllerType_OHCI, &cOhciCtrls); + if (SUCCEEDED(hrc)) { + hrc = sessionMachine->GetUSBControllerCountByType(USBControllerType_XHCI, &cXhciCtrls); + if ( SUCCEEDED(hrc) + && cOhciCtrls + cXhciCtrls == 0) + { + /* If there's nothing, enable OHCI (always available). */ + ComPtr 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(hrc)) + fEnableUsb = true; + } + else if (!RTStrICmp(ValueUnion.psz, "none")) + { + CHECK_ERROR(sessionMachine, COMSETTER(KeyboardHIDType)(KeyboardHIDType_None)); + if (SUCCEEDED(hrc)) + fEnableUsb = true; + } + else + { + errorArgument(ModifyVM::tr("Invalid type '%s' specfied for keyboard"), ValueUnion.psz); + hrc = E_FAIL; + } + if (fEnableUsb) + { + /* Make sure either the OHCI or xHCI controller is enabled. */ + ULONG cOhciCtrls = 0; + ULONG cXhciCtrls = 0; + hrc = sessionMachine->GetUSBControllerCountByType(USBControllerType_OHCI, &cOhciCtrls); + if (SUCCEEDED(hrc)) { + hrc = sessionMachine->GetUSBControllerCountByType(USBControllerType_XHCI, &cXhciCtrls); + if ( SUCCEEDED(hrc) + && cOhciCtrls + cXhciCtrls == 0) + { + /* If there's nothing, enable OHCI (always available). */ + ComPtr UsbCtl; + CHECK_ERROR(sessionMachine, AddUSBController(Bstr("OHCI").raw(), USBControllerType_OHCI, + UsbCtl.asOutParam())); + } + } + } + break; + } + + case MODIFYVM_UARTMODE: + { + ComPtr 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(ModifyVM::tr("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 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(ModifyVM::tr("Invalid argument to '%s'"), + GetOptState.pDef->pszLong); + break; + } + + case MODIFYVM_UART: + { + ComPtr 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(ModifyVM::tr("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(ModifyVM::tr("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 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 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(ModifyVM::tr("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(ModifyVM::tr("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 audioSettings; + CHECK_ERROR_BREAK(sessionMachine, COMGETTER(AudioSettings)(audioSettings.asOutParam())); + ComPtr audioAdapter; + CHECK_ERROR_BREAK(audioSettings, COMGETTER(Adapter)(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(ModifyVM::tr("Invalid --audiocontroller argument '%s'"), ValueUnion.psz); + hrc = E_FAIL; + } + break; + } + + case MODIFYVM_AUDIOCODEC: + { + ComPtr audioSettings; + CHECK_ERROR_BREAK(sessionMachine, COMGETTER(AudioSettings)(audioSettings.asOutParam())); + ComPtr audioAdapter; + CHECK_ERROR_BREAK(audioSettings, COMGETTER(Adapter)(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(ModifyVM::tr("Invalid --audiocodec argument '%s'"), ValueUnion.psz); + hrc = E_FAIL; + } + break; + } + + case MODIFYVM_AUDIODRIVER: + RT_FALL_THROUGH(); + case MODIFYVM_AUDIO: /** @todo Deprecated; remove. */ + { + if (c == MODIFYVM_AUDIO) + RTStrmPrintf(g_pStdErr, + ModifyVM::tr("Warning: --audio is deprecated and will be removed soon. Use --audio-driver instead!\n")); + + ComPtr audioSettings; + CHECK_ERROR_BREAK(sessionMachine, COMGETTER(AudioSettings)(audioSettings.asOutParam())); + ComPtr audioAdapter; + CHECK_ERROR_BREAK(audioSettings, COMGETTER(Adapter)(audioAdapter.asOutParam())); + ASSERT(audioAdapter); + /* disable? */ + if ( !RTStrICmp(ValueUnion.psz, "none") + || !RTStrICmp(ValueUnion.psz, "null")) + CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_Null)); + else if (!RTStrICmp(ValueUnion.psz, "default")) + CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_Default)); +#ifdef RT_OS_WINDOWS +# ifdef VBOX_WITH_WINMM + else if (!RTStrICmp(ValueUnion.psz, "winmm")) + CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_WinMM)); +# endif + else if (!RTStrICmp(ValueUnion.psz, "dsound")) + CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_DirectSound)); + else if (!RTStrICmp(ValueUnion.psz, "was")) + CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_WAS)); +#endif /* RT_OS_WINDOWS */ +#ifdef VBOX_WITH_AUDIO_OSS + else if (!RTStrICmp(ValueUnion.psz, "oss")) + CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_OSS)); +#endif +#ifdef VBOX_WITH_AUDIO_ALSA + else if (!RTStrICmp(ValueUnion.psz, "alsa")) + CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_ALSA)); +#endif +#ifdef VBOX_WITH_AUDIO_PULSE + else if (!RTStrICmp(ValueUnion.psz, "pulse")) + CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_Pulse)); +#endif +#ifdef RT_OS_DARWIN + else if (!RTStrICmp(ValueUnion.psz, "coreaudio")) + CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_CoreAudio)); +#endif /* !RT_OS_DARWIN */ + else + { + errorArgument(ModifyVM::tr("Invalid %s argument '%s'"), + c == MODIFYVM_AUDIO ? "--audio" : "--audio-driver", ValueUnion.psz); + hrc = E_FAIL; + } + + if ( SUCCEEDED(hrc) + && c == MODIFYVM_AUDIO) /* To keep the original behavior until we remove the command. */ + CHECK_ERROR(audioAdapter, COMSETTER(Enabled)(RTStrICmp(ValueUnion.psz, "none") == false ? false : true)); + + break; + } + + case MODIFYVM_AUDIOENABLED: + { + ComPtr audioSettings; + CHECK_ERROR_BREAK(sessionMachine, COMGETTER(AudioSettings)(audioSettings.asOutParam())); + ComPtr audioAdapter; + CHECK_ERROR_BREAK(audioSettings, COMGETTER(Adapter)(audioAdapter.asOutParam())); + ASSERT(audioAdapter); + + CHECK_ERROR(audioAdapter, COMSETTER(Enabled)(ValueUnion.f)); + break; + } + + case MODIFYVM_AUDIOIN: + { + ComPtr audioSettings; + CHECK_ERROR_BREAK(sessionMachine, COMGETTER(AudioSettings)(audioSettings.asOutParam())); + ComPtr audioAdapter; + CHECK_ERROR_BREAK(audioSettings, COMGETTER(Adapter)(audioAdapter.asOutParam())); + ASSERT(audioAdapter); + + CHECK_ERROR(audioAdapter, COMSETTER(EnabledIn)(ValueUnion.f)); + break; + } + + case MODIFYVM_AUDIOOUT: + { + ComPtr audioSettings; + CHECK_ERROR_BREAK(sessionMachine, COMGETTER(AudioSettings)(audioSettings.asOutParam())); + ComPtr audioAdapter; + CHECK_ERROR_BREAK(audioSettings, COMGETTER(Adapter)(audioAdapter.asOutParam())); + ASSERT(audioAdapter); + + CHECK_ERROR(audioAdapter, COMSETTER(EnabledOut)(ValueUnion.f)); + break; + } + +#ifdef VBOX_WITH_SHARED_CLIPBOARD + case MODIFYVM_CLIPBOARD_MODE: + { + 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(ModifyVM::tr("Invalid --clipboard-mode argument '%s'"), ValueUnion.psz); + hrc = E_FAIL; + } + if (SUCCEEDED(hrc)) + { + CHECK_ERROR(sessionMachine, COMSETTER(ClipboardMode)(mode)); + } + break; + } + +# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS + case MODIFYVM_CLIPBOARD_FILE_TRANSFERS: + { + BOOL fEnabled = false; /* Shut up MSC */ + if (!RTStrICmp(ValueUnion.psz, "enabled")) + fEnabled = true; + else if (!RTStrICmp(ValueUnion.psz, "disabled")) + fEnabled = false; + else + { + errorArgument(ModifyVM::tr("Invalid --clipboard-file-transfers argument '%s'"), ValueUnion.psz); + hrc = E_FAIL; + } + if (SUCCEEDED(hrc)) + { + CHECK_ERROR(sessionMachine, COMSETTER(ClipboardFileTransfersEnabled)(fEnabled)); + } + break; + } +# endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */ +#endif /* VBOX_WITH_SHARED_CLIPBOARD */ + + 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(ModifyVM::tr("Invalid --draganddrop argument '%s'"), ValueUnion.psz); + hrc = E_FAIL; + } + if (SUCCEEDED(hrc)) + { + CHECK_ERROR(sessionMachine, COMSETTER(DnDMode)(mode)); + } + break; + } + + case MODIFYVM_VRDE_EXTPACK: + { + ComPtr 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 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(ModifyVM::tr("Invalid --vrdeproperty argument '%s'"), ValueUnion.psz); + hrc = E_FAIL; + break; + } + RTStrFree(pszProperty); + } + else + { + RTStrmPrintf(g_pStdErr, ModifyVM::tr("Error: Failed to allocate memory for VRDE property '%s'\n"), + ValueUnion.psz); + hrc = E_FAIL; + } + } + break; + } + + case MODIFYVM_VRDPPORT: + vrdeWarningDeprecatedOption("port"); + RT_FALL_THRU(); + + case MODIFYVM_VRDEPORT: + { + ComPtr 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 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 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(ModifyVM::tr("Invalid --vrdeauthtype argument '%s'"), ValueUnion.psz); + hrc = E_FAIL; + } + break; + } + + case MODIFYVM_VRDEAUTHLIBRARY: + { + ComPtr 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 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 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 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 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 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(ModifyVM::tr("Missing or invalid argument to '%s'"), + GetOptState.pDef->pszLong); + const char *pszNewName = ValueUnion.psz; + + SafeIfaceArray ctrls; + CHECK_ERROR(sessionMachine, COMGETTER(USBControllers)(ComSafeArrayAsOutParam(ctrls))); + bool fRenamed = false; + for (size_t i = 0; i < ctrls.size(); i++) + { + ComPtr 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(ModifyVM::tr("Invalid --usbrename parameters, nothing renamed")); + hrc = E_FAIL; + } + break; + } + + case MODIFYVM_USBXHCI: + { + ULONG cXhciCtrls = 0; + hrc = sessionMachine->GetUSBControllerCountByType(USBControllerType_XHCI, &cXhciCtrls); + if (SUCCEEDED(hrc)) + { + if (!cXhciCtrls && ValueUnion.f) + { + ComPtr UsbCtl; + CHECK_ERROR(sessionMachine, AddUSBController(Bstr("xHCI").raw(), USBControllerType_XHCI, + UsbCtl.asOutParam())); + } + else if (cXhciCtrls && !ValueUnion.f) + { + SafeIfaceArray ctrls; + CHECK_ERROR(sessionMachine, COMGETTER(USBControllers)(ComSafeArrayAsOutParam(ctrls))); + for (size_t i = 0; i < ctrls.size(); i++) + { + ComPtr 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; + hrc = sessionMachine->GetUSBControllerCountByType(USBControllerType_EHCI, &cEhciCtrls); + if (SUCCEEDED(hrc)) + { + if (!cEhciCtrls && ValueUnion.f) + { + ComPtr UsbCtl; + CHECK_ERROR(sessionMachine, AddUSBController(Bstr("EHCI").raw(), USBControllerType_EHCI, + UsbCtl.asOutParam())); + } + else if (cEhciCtrls && !ValueUnion.f) + { + SafeIfaceArray ctrls; + CHECK_ERROR(sessionMachine, COMGETTER(USBControllers)(ComSafeArrayAsOutParam(ctrls))); + for (size_t i = 0; i < ctrls.size(); i++) + { + ComPtr 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; + hrc = sessionMachine->GetUSBControllerCountByType(USBControllerType_OHCI, &cOhciCtrls); + if (SUCCEEDED(hrc)) + { + if (!cOhciCtrls && ValueUnion.f) + { + ComPtr UsbCtl; + CHECK_ERROR(sessionMachine, AddUSBController(Bstr("OHCI").raw(), USBControllerType_OHCI, + UsbCtl.asOutParam())); + } + else if (cOhciCtrls && !ValueUnion.f) + { + SafeIfaceArray ctrls; + CHECK_ERROR(sessionMachine, COMGETTER(USBControllers)(ComSafeArrayAsOutParam(ctrls))); + for (size_t i = 0; i < ctrls.size(); i++) + { + ComPtr 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) + hrc = 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_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, ModifyVM::tr("*** I/O APIC must be enabled for ICH9, enabling. ***\n")); + CHECK_ERROR(biosSettings, COMSETTER(IOAPICEnabled)(TRUE)); + } + } + else + { + errorArgument(ModifyVM::tr("Invalid --chipset argument '%s' (valid: piix3,ich9)"), ValueUnion.psz); + hrc = E_FAIL; + } + break; + } +#if defined(VBOX_WITH_IOMMU_AMD) || defined(VBOX_WITH_IOMMU_INTEL) + case MODIFYVM_IOMMU: + { + if ( !RTStrICmp(ValueUnion.psz, "none") + || !RTStrICmp(ValueUnion.psz, "disabled")) + CHECK_ERROR(sessionMachine, COMSETTER(IommuType)(IommuType_None)); + else if (!RTStrICmp(ValueUnion.psz, "amd")) + CHECK_ERROR(sessionMachine, COMSETTER(IommuType)(IommuType_AMD)); + else if (!RTStrICmp(ValueUnion.psz, "intel")) + { +#ifdef VBOX_WITH_IOMMU_INTEL + CHECK_ERROR(sessionMachine, COMSETTER(IommuType)(IommuType_Intel)); +#else + errorArgument(ModifyVM::tr("Invalid --iommu argument '%s' (valid: none,amd,automatic)"), ValueUnion.psz); + hrc = E_FAIL; +#endif + } + else if (!RTStrICmp(ValueUnion.psz, "automatic")) + { + CHECK_ERROR(sessionMachine, COMSETTER(IommuType)(IommuType_Automatic)); +#ifndef VBOX_WITH_IOMMU_INTEL + RTStrmPrintf(g_pStdErr, + ModifyVM::tr("Warning: On Intel hosts, 'automatic' will not enable an IOMMU since the Intel IOMMU device is not supported yet.\n")); +#endif + } + else + { + errorArgument(ModifyVM::tr("Invalid --iommu argument '%s'"), ValueUnion.psz); + hrc = E_FAIL; + } + break; + } +#endif +#if defined(VBOX_WITH_TPM) + case MODIFYVM_TPM_TYPE: + { + ComPtr tpm; + sessionMachine->COMGETTER(TrustedPlatformModule)(tpm.asOutParam()); + + if ( !RTStrICmp(ValueUnion.psz, "none") + || !RTStrICmp(ValueUnion.psz, "disabled")) + CHECK_ERROR(tpm, COMSETTER(Type)(TpmType_None)); + else if (!RTStrICmp(ValueUnion.psz, "1.2")) + CHECK_ERROR(tpm, COMSETTER(Type)(TpmType_v1_2)); + else if (!RTStrICmp(ValueUnion.psz, "2.0")) + CHECK_ERROR(tpm, COMSETTER(Type)(TpmType_v2_0)); + else if (!RTStrICmp(ValueUnion.psz, "host")) + CHECK_ERROR(tpm, COMSETTER(Type)(TpmType_Host)); + else if (!RTStrICmp(ValueUnion.psz, "swtpm")) + CHECK_ERROR(tpm, COMSETTER(Type)(TpmType_Swtpm)); + else + { + errorArgument(ModifyVM::tr("Invalid --tpm-type argument '%s'"), ValueUnion.psz); + hrc = E_FAIL; + } + break; + } + + case MODIFYVM_TPM_LOCATION: + { + ComPtr tpm; + sessionMachine->COMGETTER(TrustedPlatformModule)(tpm.asOutParam()); + + CHECK_ERROR(tpm, COMSETTER(Location)(Bstr(ValueUnion.psz).raw())); + break; + } +#endif +#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 recordingSettings; + CHECK_ERROR_BREAK(sessionMachine, COMGETTER(RecordingSettings)(recordingSettings.asOutParam())); + SafeIfaceArray 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(pGraphicsAdapter, COMGETTER(MonitorCount)(&cMonitors)); + com::SafeArray screens(cMonitors); + if (RT_FAILURE(parseScreens(ValueUnion.psz, &screens))) + { + errorArgument(ModifyVM::tr("Invalid list of screens specified\n")); + hrc = 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(ModifyVM::tr("Cannot convert filename \"%s\" to absolute path\n"), ValueUnion.psz); + hrc = 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(ModifyVM::tr("Error parsing video resolution '%s' (expected x)"), + ValueUnion.psz); + hrc = E_FAIL; + break; + } + uint32_t uHeight = 0; + vrc = RTStrToUInt32Ex(pszNext+1, NULL, 0, &uHeight); + if (vrc != VINF_SUCCESS) + { + errorArgument(ModifyVM::tr("Error parsing video resolution '%s' (expected x)"), + ValueUnion.psz); + hrc = 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(ModifyVM::tr("Invalid --autostop-type argument '%s' (valid: disabled, savestate, poweroff, acpishutdown)"), + ValueUnion.psz); + hrc = E_FAIL; + } + + if (SUCCEEDED(hrc)) + 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(ModifyVM::tr("Invalid --pciattach argument '%s' (valid: 'HB:HD.HF@GB:GD.GF' or just 'HB:HD.HF')"), + ValueUnion.psz); + hrc = 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(ModifyVM::tr("Invalid --pcidetach argument '%s' (valid: 'HB:HD.HF')"), ValueUnion.psz); + hrc = 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; + } + + case MODIFYVM_VMPROC_PRIORITY: + { + VMProcPriority_T enmPriority = nameToVMProcPriority(ValueUnion.psz); + if (enmPriority == VMProcPriority_Invalid) + { + errorArgument(ModifyVM::tr("Invalid --vm-process-priority '%s'"), ValueUnion.psz); + hrc = E_FAIL; + } + else + { + CHECK_ERROR(sessionMachine, COMSETTER(VMProcessPriority)(enmPriority)); + } + break; + } + + case MODIFYVM_TESTING_ENABLED: + hrc = setExtraData(sessionMachine, "VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", ValueUnion.f ? "1" : ""); + break; + + case MODIFYVM_TESTING_MMIO: + hrc = setExtraData(sessionMachine, "VBoxInternal/Devices/VMMDev/0/Config/TestingMMIO", ValueUnion.f ? "1" : ""); + break; + + case MODIFYVM_TESTING_CFG_DWORD: + if (GetOptState.uIndex <= 9) + { + char szVar[128]; + RTStrPrintf(szVar, sizeof(szVar), "VBoxInternal/Devices/VMMDev/0/Config/TestingCfgDword%u", + GetOptState.uIndex); + char szValue[32]; + RTStrPrintf(szValue, sizeof(szValue), "%u", ValueUnion.u32); + hrc = setExtraData(sessionMachine, szVar, szValue); + } + else + hrc = errorArgumentHr(ModifyVM::tr("--testing-cfg-dword index %u is out of range: 0 thru 9"), + GetOptState.uIndex); + break; + + case MODIFYVM_GUEST_DEBUG_PROVIDER: + { + ComPtr gstDbgCtrl; + CHECK_ERROR_BREAK(sessionMachine, COMGETTER(GuestDebugControl)(gstDbgCtrl.asOutParam())); + + GuestDebugProvider_T enmDebugProvider = GuestDebugProvider_None; + + if (!RTStrICmp(ValueUnion.psz, "none")) + enmDebugProvider = GuestDebugProvider_None; + else if (!RTStrICmp(ValueUnion.psz, "native")) + enmDebugProvider = GuestDebugProvider_Native; + else if (!RTStrICmp(ValueUnion.psz, "gdb")) + enmDebugProvider = GuestDebugProvider_GDB; + else if (!RTStrICmp(ValueUnion.psz, "kd")) + enmDebugProvider = GuestDebugProvider_KD; + else + { + errorArgument(ModifyVM::tr("Invalid --guest-debug-provider '%s' (valid: none, native, gdb, kd)"), + ValueUnion.psz); + hrc = E_FAIL; + } + + if (SUCCEEDED(hrc)) + CHECK_ERROR(gstDbgCtrl, COMSETTER(DebugProvider)(enmDebugProvider)); + break; + } + + case MODIFYVM_GUEST_DEBUG_IO_PROVIDER: + { + ComPtr gstDbgCtrl; + CHECK_ERROR_BREAK(sessionMachine, COMGETTER(GuestDebugControl)(gstDbgCtrl.asOutParam())); + + GuestDebugIoProvider_T enmDebugIoProvider = GuestDebugIoProvider_None; + + if (!RTStrICmp(ValueUnion.psz, "none")) + enmDebugIoProvider = GuestDebugIoProvider_None; + else if (!RTStrICmp(ValueUnion.psz, "tcp")) + enmDebugIoProvider = GuestDebugIoProvider_TCP; + else if (!RTStrICmp(ValueUnion.psz, "udp")) + enmDebugIoProvider = GuestDebugIoProvider_UDP; + else if (!RTStrICmp(ValueUnion.psz, "ipc")) + enmDebugIoProvider = GuestDebugIoProvider_IPC; + else + { + errorArgument(ModifyVM::tr("Invalid --guest-debug-io-provider '%s' (valid: none, tcp, udp, ipc)"), + ValueUnion.psz); + hrc = E_FAIL; + } + + if (SUCCEEDED(hrc)) + CHECK_ERROR(gstDbgCtrl, COMSETTER(DebugIoProvider)(enmDebugIoProvider)); + break; + } + + case MODIFYVM_GUEST_DEBUG_ADDRESS: + { + ComPtr gstDbgCtrl; + CHECK_ERROR_BREAK(sessionMachine, COMGETTER(GuestDebugControl)(gstDbgCtrl.asOutParam())); + + Bstr bstr(ValueUnion.psz); + CHECK_ERROR(gstDbgCtrl, COMSETTER(DebugAddress)(bstr.raw())); + break; + } + + case MODIFYVM_GUEST_DEBUG_PORT: + { + ComPtr gstDbgCtrl; + CHECK_ERROR_BREAK(sessionMachine, COMGETTER(GuestDebugControl)(gstDbgCtrl.asOutParam())); + CHECK_ERROR(gstDbgCtrl, COMSETTER(DebugPort)(ValueUnion.u32)); + break; + } + + default: + errorGetOpt(c, &ValueUnion); + hrc = E_FAIL; + break; + } + } + + /* commit changes */ + if (SUCCEEDED(hrc)) + CHECK_ERROR(sessionMachine, SaveSettings()); + + /* it's important to always close sessions */ + a->session->UnlockMachine(); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageNATNetwork.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageNATNetwork.cpp new file mode 100644 index 00000000..8f911fec --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageNATNetwork.cpp @@ -0,0 +1,702 @@ +/* $Id: VBoxManageNATNetwork.cpp $ */ +/** @file + * VBoxManage - Implementation of NAT Network command command. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include +#include + +#ifndef RT_OS_WINDOWS +# include +#else +/* from */ +# define INET6_ADDRSTRLEN 65 +#endif + +#define IPv6 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "VBoxManage.h" +#include "VBoxPortForwardString.h" + + +DECLARE_TRANSLATION_CONTEXT(Nat); + +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 VPF2DELETE; +typedef VPF2DELETE::const_iterator VPF2DELETEITERATOR; + +typedef std::vector VPF2ADD; +typedef VPF2ADD::const_iterator VPF2ADDITERATOR; + +typedef std::vector LOOPBACK2DELETEADD; +typedef LOOPBACK2DELETEADD::iterator LOOPBACK2DELETEADDITERATOR; + +static HRESULT printNATNetwork(const ComPtr &pNATNet, + bool fLong = true) +{ + HRESULT hrc; + + do + { + Bstr strVal; + BOOL fVal; + + CHECK_ERROR_BREAK(pNATNet, COMGETTER(NetworkName)(strVal.asOutParam())); + RTPrintf(Nat::tr("Name: %ls\n"), strVal.raw()); + + if (fLong) + { + /* + * What does it even mean for a natnet to be disabled? + * (rhetorical question). Anyway, don't print it unless + * asked for a complete dump. + */ + CHECK_ERROR_BREAK(pNATNet, COMGETTER(Enabled)(&fVal)); + RTPrintf(Nat::tr("Enabled: %s\n"), fVal ? Nat::tr("Yes") : Nat::tr("No")); + } + + CHECK_ERROR_BREAK(pNATNet, COMGETTER(Network)(strVal.asOutParam())); + RTPrintf(Nat::tr("Network: %ls\n"), strVal.raw()); + + CHECK_ERROR_BREAK(pNATNet, COMGETTER(Gateway)(strVal.asOutParam())); + RTPrintf(Nat::tr("Gateway: %ls\n"), strVal.raw()); + + CHECK_ERROR_BREAK(pNATNet, COMGETTER(NeedDhcpServer)(&fVal)); + RTPrintf(Nat::tr("DHCP Server: %s\n"), fVal ? Nat::tr("Yes") : Nat::tr("No")); + + CHECK_ERROR_BREAK(pNATNet, COMGETTER(IPv6Enabled)(&fVal)); + RTPrintf("IPv6: %s\n", fVal ? Nat::tr("Yes") : Nat::tr("No")); + + CHECK_ERROR_BREAK(pNATNet, COMGETTER(IPv6Prefix)(strVal.asOutParam())); + RTPrintf(Nat::tr("IPv6 Prefix: %ls\n"), strVal.raw()); + + CHECK_ERROR_BREAK(pNATNet, COMGETTER(AdvertiseDefaultIPv6RouteEnabled)(&fVal)); + RTPrintf(Nat::tr("IPv6 Default: %s\n"), fVal ? Nat::tr("Yes") : Nat::tr("No")); + + + if (fLong) + { + com::SafeArray strs; + +#define PRINT_STRING_ARRAY(title) do { \ + if (strs.size() > 0) \ + { \ + RTPrintf(title); \ + for (size_t j = 0; j < strs.size(); ++j) \ + RTPrintf(" %s\n", Utf8Str(strs[j]).c_str()); \ + } \ + } while (0) + + CHECK_ERROR_BREAK(pNATNet, COMGETTER(PortForwardRules4)(ComSafeArrayAsOutParam(strs))); + PRINT_STRING_ARRAY(Nat::tr("Port-forwarding (ipv4)\n")); + strs.setNull(); + + CHECK_ERROR(pNATNet, COMGETTER(PortForwardRules6)(ComSafeArrayAsOutParam(strs))); + PRINT_STRING_ARRAY(Nat::tr("Port-forwarding (ipv6)\n")); + strs.setNull(); + + CHECK_ERROR(pNATNet, COMGETTER(LocalMappings)(ComSafeArrayAsOutParam(strs))); + PRINT_STRING_ARRAY(Nat::tr("loopback mappings (ipv4)\n")); + strs.setNull(); + +#undef PRINT_STRING_ARRAY + } + + RTPrintf("\n"); + } while (0); + + return hrc; +} + +static RTEXITCODE handleNATList(HandlerArg *a) +{ + HRESULT hrc; + + RTPrintf(Nat::tr("NAT Networks:\n\n")); + + const char *pszFilter = NULL; + if (a->argc > 1) + pszFilter = a->argv[1]; + + size_t cFound = 0; + + com::SafeIfaceArray arrNetNets; + CHECK_ERROR(a->virtualBox, COMGETTER(NATNetworks)(ComSafeArrayAsOutParam(arrNetNets))); + for (size_t i = 0; i < arrNetNets.size(); ++i) + { + ComPtr pNATNet = arrNetNets[i]; + + if (pszFilter) + { + Bstr strVal; + CHECK_ERROR_BREAK(pNATNet, COMGETTER(NetworkName)(strVal.asOutParam())); + + Utf8Str strValUTF8(strVal); + if (!RTStrSimplePatternMatch(pszFilter, strValUTF8.c_str())) + continue; + } + + hrc = printNATNetwork(pNATNet); + if (FAILED(hrc)) + break; + + cFound++; + } + + if (SUCCEEDED(hrc)) + RTPrintf(Nat::tr("%zu %s found\n"), cFound, cFound == 1 ? Nat::tr("network") : Nat::tr("networks", "", cFound)); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +static RTEXITCODE handleOp(HandlerArg *a, OPCODE enmCode) +{ + if (a->argc - 1 <= 1) + return errorSyntax(Nat::tr("Not enough parameters")); + + const char *pNetName = NULL; + const char *pPrefixIPv4 = NULL; + const char *pPrefixIPv6 = NULL; + int enable = -1; + int dhcp = -1; + int ipv6 = -1; + int ipv6_default = -1; + + VPF2DELETE vPfName2Delete; + VPF2ADD vPf2Add; + + LOOPBACK2DELETEADD vLoopback2Delete; + LOOPBACK2DELETEADD vLoopback2Add; + + LONG loopback6Offset = 0; /* ignore me */ + + enum + { + kNATNetworkIota = 1000, + kNATNetwork_IPv6Default, + kNATNetwork_IPv6Prefix, + }; + + static const RTGETOPTDEF g_aNATNetworkIPOptions[] = + { + { "--netname", 't', RTGETOPT_REQ_STRING }, + { "--network", 'n', RTGETOPT_REQ_STRING }, /* old name */ + { "--ipv4-prefix", 'n', RTGETOPT_REQ_STRING }, /* new name */ + { "--dhcp", 'h', RTGETOPT_REQ_BOOL }, + { "--ipv6", '6', RTGETOPT_REQ_BOOL }, /* old name */ + { "--ipv6-default", kNATNetwork_IPv6Default, RTGETOPT_REQ_BOOL }, + { "--ipv6-enable", '6', RTGETOPT_REQ_BOOL }, /* new name */ + { "--ipv6-prefix", kNATNetwork_IPv6Prefix, RTGETOPT_REQ_STRING }, + { "--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(Nat::tr("You can only specify --netname only once.")); + pNetName = ValueUnion.psz; + break; + + case 'n': // --network + if (pPrefixIPv4) + return errorSyntax(Nat::tr("You can only specify --network only once.")); + pPrefixIPv4 = ValueUnion.psz; + break; + + case 'e': // --enable + if (enable >= 0) + return errorSyntax(Nat::tr("You can specify either --enable or --disable once.")); + enable = 1; + break; + + case 'd': // --disable + if (enable >= 0) + return errorSyntax(Nat::tr("You can specify either --enable or --disable once.")); + enable = 0; + break; + + case 'h': + if (dhcp != -1) + return errorSyntax(Nat::tr("You can specify --dhcp only once.")); + dhcp = ValueUnion.f; + break; + + case '6': + if (ipv6 != -1) + return errorSyntax(Nat::tr("You can specify --ipv6 only once.")); + ipv6 = ValueUnion.f; + break; + + case kNATNetwork_IPv6Prefix: + if (pPrefixIPv6) + return errorSyntax(Nat::tr("You can specify --ipv6-prefix only once.")); + pPrefixIPv6 = ValueUnion.psz; + break; + + case kNATNetwork_IPv6Default: // XXX: uwe + if (ipv6_default != -1) + return errorSyntax(Nat::tr("You can specify --ipv6-default only once.")); + ipv6_default = ValueUnion.f; + break; + + case 'L': /* ipv6 loopback */ + case 'l': /* ipv4 loopback */ + if (RTStrCmp(ValueUnion.psz, "delete") == 0) + { + /* deletion */ + if (enmCode != OP_MODIFY) + errorSyntax(Nat::tr("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(Nat::tr("Not enough parŠ°meters\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(Nat::tr("Invalid port-forward rule %s\n"), ValueUnion.psz); + + vPf2Add.push_back(Pfr); + } + else + { + /* deletion */ + if (enmCode != OP_MODIFY) + return errorSyntax(Nat::tr("Port-forward could be deleted on modify\n")); + + RTGETOPTUNION NamePf2DeleteUnion; + int vrc = RTGetOptFetchValue(&GetState, &NamePf2DeleteUnion, RTGETOPT_REQ_STRING); + if (RT_FAILURE(vrc)) + return errorSyntax(Nat::tr("Not enough parŠ°meters\n")); + + if (strlen(NamePf2DeleteUnion.psz) > PF_NAMELEN) + return errorSyntax(Nat::tr("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(c, &ValueUnion); + } + } + + if (!pNetName) + return errorSyntax(Nat::tr("You need to specify the --netname option")); + /* verification */ + switch (enmCode) + { + case OP_ADD: + if (!pPrefixIPv4) + return errorSyntax(Nat::tr("You need to specify the --network option")); + break; + case OP_MODIFY: + case OP_REMOVE: + case OP_START: + case OP_STOP: + break; + default: + AssertMsgFailedReturn((Nat::tr("Unknown operation (:%d)"), enmCode), RTEXITCODE_FAILURE); + } + + HRESULT hrc; + Bstr NetName; + NetName = Bstr(pNetName); + + ComPtr net; + hrc = a->virtualBox->FindNATNetworkByName(NetName.mutableRaw(), net.asOutParam()); + if (enmCode == OP_ADD) + { + if (SUCCEEDED(hrc)) + return errorArgument(Nat::tr("NATNetwork server already exists")); + + CHECK_ERROR(a->virtualBox, CreateNATNetwork(NetName.raw(), net.asOutParam())); + if (FAILED(hrc)) + return errorArgument(Nat::tr("Failed to create the NAT network service")); + } + else if (FAILED(hrc)) + return errorArgument(Nat::tr("NATNetwork server does not exist")); + + switch (enmCode) + { + case OP_ADD: + case OP_MODIFY: + { + if (pPrefixIPv4) + { + CHECK_ERROR(net, COMSETTER(Network)(Bstr(pPrefixIPv4).raw())); + if (FAILED(hrc)) + return errorArgument(Nat::tr("Failed to set configuration")); + } + if (dhcp >= 0) + { + CHECK_ERROR(net, COMSETTER(NeedDhcpServer) ((BOOL)dhcp)); + if (FAILED(hrc)) + return errorArgument(Nat::tr("Failed to set configuration")); + } + + /* + * If we are asked to disable IPv6, do it early so that + * the same command can also set IPv6 prefix to empty if + * it so wishes. + */ + if (ipv6 == 0) + { + CHECK_ERROR(net, COMSETTER(IPv6Enabled)(FALSE)); + if (FAILED(hrc)) + return errorArgument(Nat::tr("Failed to set configuration")); + } + + if (pPrefixIPv6) + { + CHECK_ERROR(net, COMSETTER(IPv6Prefix)(Bstr(pPrefixIPv6).raw())); + if (FAILED(hrc)) + return errorArgument(Nat::tr("Failed to set configuration")); + } + + /* + * If we are asked to enable IPv6, do it late, so that the + * same command can also set IPv6 prefix. + */ + if (ipv6 > 0) + { + CHECK_ERROR(net, COMSETTER(IPv6Enabled)(TRUE)); + if (FAILED(hrc)) + return errorArgument(Nat::tr("Failed to set configuration")); + } + + if (ipv6_default != -1) + { + BOOL fIPv6Default = RT_BOOL(ipv6_default); + CHECK_ERROR(net, COMSETTER(AdvertiseDefaultIPv6RouteEnabled)(fIPv6Default)); + if (FAILED(hrc)) + return errorArgument(Nat::tr("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(hrc)) + return errorArgument(Nat::tr("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(hrc)) + return errorArgument(Nat::tr("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(Nat::tr("invalid loopback string"))); + + address = it->substr(0, pos); + strOffset = it->substr(pos + 1); + + lOffset = RTStrToUInt32(strOffset.c_str()); + AssertReturn(lOffset > 0, errorArgument(Nat::tr("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(hrc)) + return errorArgument(Nat::tr("Failed to set configuration")); + } + break; + } + case OP_REMOVE: + { + CHECK_ERROR(a->virtualBox, RemoveNATNetwork(net)); + if (FAILED(hrc)) + return errorArgument(Nat::tr("Failed to remove nat network")); + break; + } + case OP_START: + { + CHECK_ERROR(net, Start()); + if (FAILED(hrc)) + return errorArgument(Nat::tr("Failed to start network")); + break; + } + case OP_STOP: + { + CHECK_ERROR(net, Stop()); + if (FAILED(hrc)) + return errorArgument(Nat::tr("Failed to stop network")); + break; + } + default:; + } + return RTEXITCODE_SUCCESS; +} + + +/* + * VBoxManage natnetwork ... + */ +RTEXITCODE handleNATNetwork(HandlerArg *a) +{ + if (a->argc < 1) + return errorSyntax(Nat::tr("Not enough parameters")); + + RTEXITCODE rcExit; + if (strcmp(a->argv[0], "modify") == 0) + { + setCurrentSubcommand(HELP_SCOPE_NATNETWORK_MODIFY); + rcExit = handleOp(a, OP_MODIFY); + } + else if (strcmp(a->argv[0], "add") == 0) + { + setCurrentSubcommand(HELP_SCOPE_NATNETWORK_ADD); + rcExit = handleOp(a, OP_ADD); + } + else if (strcmp(a->argv[0], "remove") == 0) + { + setCurrentSubcommand(HELP_SCOPE_NATNETWORK_REMOVE); + rcExit = handleOp(a, OP_REMOVE); + } + else if (strcmp(a->argv[0], "start") == 0) + { + setCurrentSubcommand(HELP_SCOPE_NATNETWORK_START); + rcExit = handleOp(a, OP_START); + } + else if (strcmp(a->argv[0], "stop") == 0) + { + setCurrentSubcommand(HELP_SCOPE_NATNETWORK_STOP); + rcExit = handleOp(a, OP_STOP); + } + else if (strcmp(a->argv[0], "list") == 0) + { + setCurrentSubcommand(HELP_SCOPE_NATNETWORK_LIST); + rcExit = handleNATList(a); + } + else + rcExit = errorSyntax(Nat::tr("Invalid parameter '%s'"), a->argv[0]); + return rcExit; +} + + +/* + * VBoxManage list natnetworks ... + */ +RTEXITCODE listNATNetworks(bool fLong, bool fSorted, + const ComPtr &pVirtualBox) +{ + HRESULT hrc; + + com::SafeIfaceArray aNets; + CHECK_ERROR_RET(pVirtualBox, + COMGETTER(NATNetworks)(ComSafeArrayAsOutParam(aNets)), + RTEXITCODE_FAILURE); + + const size_t cNets = aNets.size(); + if (cNets == 0) + return RTEXITCODE_SUCCESS; + + /* + * Sort the list if necessary. The sort is indirect via an + * intermediate array of indexes. + */ + std::vector vIndexes(cNets); + for (size_t i = 0; i < cNets; ++i) + vIndexes[i] = i; + + if (fSorted) + { + std::vector vBstrNames(cNets); + for (size_t i = 0; i < cNets; ++i) + { + CHECK_ERROR_RET(aNets[i], + COMGETTER(NetworkName)(vBstrNames[i].asOutParam()), + RTEXITCODE_FAILURE); + } + + struct SortBy { + const std::vector &ks; + SortBy(const std::vector &aKeys) : ks(aKeys) {} + bool operator() (size_t l, size_t r) { return ks[l] < ks[r]; } + }; + + std::sort(vIndexes.begin(), vIndexes.end(), + SortBy(vBstrNames)); + } + + for (size_t i = 0; i < cNets; ++i) + { + printNATNetwork(aNets[vIndexes[i]], fLong); + } + + return RTEXITCODE_SUCCESS; +} diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageSnapshot.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageSnapshot.cpp new file mode 100644 index 00000000..69598c44 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageSnapshot.cpp @@ -0,0 +1,670 @@ +/* $Id: VBoxManageSnapshot.cpp $ */ +/** @file + * VBoxManage - The 'snapshot' command. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "VBoxManage.h" +using namespace com; + +DECLARE_TRANSLATION_CONTEXT(Snapshot); + +/** + * 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 &pMedium, + ComPtr &pThisSnapshot, + ComPtr &pCurrentSnapshot, + uint32_t uMediumLevel, + uint32_t uSnapshotLevel) +{ + HRESULT hrc; + + do + { + // get snapshot machine so we can figure out which diff image this created + ComPtr pSnapshotMachine; + CHECK_ERROR_BREAK(pThisSnapshot, COMGETTER(Machine)(pSnapshotMachine.asOutParam())); + + // get media attachments + SafeIfaceArray aAttachments; + CHECK_ERROR_BREAK(pSnapshotMachine, COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(aAttachments))); + + for (uint32_t i = 0; + i < aAttachments.size(); + ++i) + { + ComPtr pAttach(aAttachments[i]); + DeviceType_T type; + CHECK_ERROR_BREAK(pAttach, COMGETTER(Type)(&type)); + if (type == DeviceType_HardDisk) + { + ComPtr 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 aSnapshots; + CHECK_ERROR_BREAK(pThisSnapshot, COMGETTER(Children)(ComSafeArrayAsOutParam(aSnapshots))); + + for (uint32_t i = 0; + i < aSnapshots.size(); + ++i) + { + ComPtr 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 &pCurrentStateMedium, + ComPtr &pMedium, + ComPtr &pRootSnapshot, + ComPtr &pCurrentSnapshot, + uint32_t uLevel) +{ + HRESULT hrc; + 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 aChildren; + CHECK_ERROR_BREAK(pMedium, COMGETTER(Children)(ComSafeArrayAsOutParam(aChildren))); + for (uint32_t i = 0; + i < aChildren.size(); + ++i) + { + ComPtr 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 &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(c, &ValueUnion); + } + } + + ComPtr pSnapshot; + HRESULT hrc = pMachine->FindSnapshot(Bstr().raw(), pSnapshot.asOutParam()); + if (FAILED(hrc)) + { + RTPrintf(Snapshot::tr("This machine does not have any snapshots\n")); + return RTEXITCODE_FAILURE; + } + if (pSnapshot) + { + ComPtr 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 &pMachine) +{ + HRESULT hrc; + + do + { + // get root snapshot + ComPtr pSnapshot; + CHECK_ERROR_BREAK(pMachine, FindSnapshot(Bstr("").raw(), pSnapshot.asOutParam())); + + // get current snapshot + ComPtr pCurrentSnapshot; + CHECK_ERROR_BREAK(pMachine, COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam())); + + // get media attachments + SafeIfaceArray aAttachments; + CHECK_ERROR_BREAK(pMachine, COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(aAttachments))); + for (uint32_t i = 0; + i < aAttachments.size(); + ++i) + { + ComPtr pAttach(aAttachments[i]); + DeviceType_T type; + CHECK_ERROR_BREAK(pAttach, COMGETTER(Type)(&type)); + if (type == DeviceType_HardDisk) + { + ComPtr pCurrentStateMedium; + CHECK_ERROR_BREAK(pAttach, COMGETTER(Medium)(pCurrentStateMedium.asOutParam())); + + ComPtr pBaseMedium; + CHECK_ERROR_BREAK(pCurrentStateMedium, COMGETTER(Base)(pBaseMedium.asOutParam())); + + Bstr bstrBaseMediumName; + CHECK_ERROR_BREAK(pBaseMedium, COMGETTER(Name)(bstrBaseMediumName.asOutParam())); + + RTPrintf(Snapshot::tr("[%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 vrc = VINF_SUCCESS; + unsigned uUnique = 0; + while (psz && *psz && RT_SUCCESS(vrc)) + { + 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 + vrc = VERR_PARSE_ERROR; + } + if (pszComma) + psz += len + 1; + else + psz += len; + } + + if (RT_SUCCESS(vrc)) + *pUnique = (SnapshotUniqueFlags)uUnique; + return vrc; +} + +/** + * Implementation for all VBoxManage snapshot ... subcommands. + * @param a + * @return + */ +RTEXITCODE handleSnapshot(HandlerArg *a) +{ + HRESULT hrc; + +/** @todo r=bird: sub-standard command line parsing here! + * + * 'VBoxManage snapshot empty take --help' takes a snapshot rather than display + * help as you would expect. + * + */ + + /* we need at least a VM and a command */ + if (a->argc < 2) + return errorSyntax(Snapshot::tr("Not enough parameters")); + + /* the first argument must be the VM */ + Bstr bstrMachine(a->argv[0]); + ComPtr 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 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")) + { + setCurrentSubcommand(HELP_SCOPE_SNAPSHOT_TAKE); + + /* there must be a name */ + if (a->argc < 3) + { + errorSyntax(Snapshot::tr("Missing snapshot name")); + hrc = 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(hrc) + && (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(Snapshot::tr("Invalid unique name description '%s'"), Value.psz); + break; + + default: + errorGetOpt(ch, &Value); + hrc = E_FAIL; + break; + } + } + if (FAILED(hrc)) + break; + + if (enmUnique & (SnapshotUniqueFlags_Number | SnapshotUniqueFlags_Timestamp)) + { + ComPtr pSnapshot; + hrc = sessionMachine->FindSnapshot(name.raw(), + pSnapshot.asOutParam()); + if (SUCCEEDED(hrc) || (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++; + hrc = sessionMachine->FindSnapshot(tryName.raw(), + pSnapshot.asOutParam()); + if (FAILED(hrc)) + { + name = tryName; + break; + } + } + if (SUCCEEDED(hrc)) + { + errorArgument(Snapshot::tr("Failed to generate a unique snapshot name")); + hrc = E_FAIL; + break; + } + } + hrc = S_OK; + } + + ComPtr progress; + Bstr snapId; + CHECK_ERROR_BREAK(sessionMachine, TakeSnapshot(name.raw(), desc.raw(), + fPause, snapId.asOutParam(), + progress.asOutParam())); + + hrc = showProgress(progress); + if (SUCCEEDED(hrc)) + RTPrintf(Snapshot::tr("Snapshot taken. UUID: %ls\n"), snapId.raw()); + else + CHECK_PROGRESS_ERROR(progress, (Snapshot::tr("Failed to take snapshot"))); + } + else if ( (fDelete = !strcmp(a->argv[1], "delete")) + || (fRestore = !strcmp(a->argv[1], "restore")) + || (fRestoreCurrent = !strcmp(a->argv[1], "restorecurrent")) + ) + { + setCurrentSubcommand(fDelete ? HELP_SCOPE_SNAPSHOT_DELETE + : fRestore ? HELP_SCOPE_SNAPSHOT_RESTORE + : HELP_SCOPE_SNAPSHOT_RESTORECURRENT); + + if (fRestoreCurrent) + { + if (a->argc > 2) + { + errorSyntax(Snapshot::tr("Too many arguments")); + hrc = E_FAIL; + break; + } + } + /* exactly one parameter: snapshot name */ + else if (a->argc != 3) + { + errorSyntax(Snapshot::tr("Expecting snapshot name only")); + hrc = E_FAIL; + break; + } + + ComPtr pSnapshot; + + if (fRestoreCurrent) + { + CHECK_ERROR_BREAK(sessionMachine, COMGETTER(CurrentSnapshot)(pSnapshot.asOutParam())); + if (pSnapshot.isNull()) + { + RTPrintf(Snapshot::tr("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 pProgress; + + RTPrintf(Snapshot::tr("%s snapshot '%ls' (%ls)\n"), + fDelete ? Snapshot::tr("Deleting") : Snapshot::tr("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())); + } + + hrc = showProgress(pProgress); + CHECK_PROGRESS_ERROR(pProgress, (Snapshot::tr("Snapshot operation failed"))); + } + else if (!strcmp(a->argv[1], "edit")) + { + setCurrentSubcommand(HELP_SCOPE_SNAPSHOT_EDIT); + if (a->argc < 3) + { + errorSyntax(Snapshot::tr("Missing snapshot name")); + hrc = E_FAIL; + break; + } + + /* Parse the optional arguments, allowing more freedom than the + * synopsis explains. Can rename multiple snapshots and so on. */ + ComPtr pSnapshot; + static const RTGETOPTDEF s_aEditOptions[] = + { + { "--current", 'c', RTGETOPT_REQ_NOTHING }, + { "-current", 'c', RTGETOPT_REQ_NOTHING }, + { "--name", 'n', RTGETOPT_REQ_STRING }, + { "-name", 'n', RTGETOPT_REQ_STRING }, + { "-newname", 'n', RTGETOPT_REQ_STRING }, + { "--description", 'd', RTGETOPT_REQ_STRING }, + { "-description", 'd', RTGETOPT_REQ_STRING }, + { "-desc", 'd', RTGETOPT_REQ_STRING } + }; + RTGETOPTSTATE GetOptState; + RTGetOptInit(&GetOptState, a->argc, a->argv, s_aEditOptions, RT_ELEMENTS(s_aEditOptions), + 2, RTGETOPTINIT_FLAGS_NO_STD_OPTS); + int ch; + RTGETOPTUNION Value; + while ( SUCCEEDED(hrc) + && (ch = RTGetOpt(&GetOptState, &Value))) + { + switch (ch) + { + case 'c': + CHECK_ERROR_BREAK(sessionMachine, COMGETTER(CurrentSnapshot)(pSnapshot.asOutParam())); + if (pSnapshot.isNull()) + { + RTPrintf(Snapshot::tr("This machine does not have any snapshots\n")); + return RTEXITCODE_FAILURE; + } + break; + + case 'n': + CHECK_ERROR_BREAK(pSnapshot, COMSETTER(Name)(Bstr(Value.psz).raw())); + break; + + case 'd': + CHECK_ERROR_BREAK(pSnapshot, COMSETTER(Description)(Bstr(Value.psz).raw())); + break; + + case VINF_GETOPT_NOT_OPTION: + CHECK_ERROR_BREAK(sessionMachine, FindSnapshot(Bstr(Value.psz).raw(), pSnapshot.asOutParam())); + break; + + default: + errorGetOpt(ch, &Value); + hrc = E_FAIL; + break; + } + } + + if (FAILED(hrc)) + break; + } + else if (!strcmp(a->argv[1], "showvminfo")) + { + setCurrentSubcommand(HELP_SCOPE_SNAPSHOT_SHOWVMINFO); + + /* exactly one parameter: snapshot name */ + if (a->argc != 3) + { + errorSyntax(Snapshot::tr("Expecting snapshot name only")); + hrc = E_FAIL; + break; + } + + ComPtr pSnapshot; + + CHECK_ERROR_BREAK(sessionMachine, FindSnapshot(Bstr(a->argv[2]).raw(), + pSnapshot.asOutParam())); + + /* get the machine of the given snapshot */ + ComPtr pMachine2; + pSnapshot->COMGETTER(Machine)(pMachine2.asOutParam()); + showVMInfo(a->virtualBox, pMachine2, NULL, VMINFO_NONE); + } + else if (!strcmp(a->argv[1], "list")) + { + setCurrentSubcommand(HELP_SCOPE_SNAPSHOT_LIST); + hrc = 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(Snapshot::tr("Invalid parameter '%s'"), a->argv[1]); + hrc = E_FAIL; + } + } while (0); + + a->session->UnlockMachine(); + + return SUCCEEDED(hrc) ? 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..395196ad --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageStorageController.cpp @@ -0,0 +1,1306 @@ +/* $Id: VBoxManageStorageController.cpp $ */ +/** @file + * VBoxManage - The storage controller related commands. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "VBoxManage.h" +using namespace com; + +DECLARE_TRANSLATION_CONTEXT(Storage); + +// 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 hrc = 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 machine; + ComPtr storageCtl; + ComPtr systemProperties; + + RTGetOptInit(&GetState, a->argc, a->argv, g_aStorageAttachOptions, + RT_ELEMENTS(g_aStorageAttachOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS); + + while ( SUCCEEDED(hrc) + && (c = RTGetOpt(&GetState, &ValueUnion))) + { + switch (c) + { + case 's': // storage controller name + { + if (ValueUnion.psz) + pszCtl = ValueUnion.psz; + else + hrc = E_FAIL; + break; + } + + case 'p': // port + { + port = ValueUnion.u32; + break; + } + + case 'd': // device + { + device = ValueUnion.u32; + break; + } + + case 'm': // medium |iSCSI> + { + if (ValueUnion.psz) + pszMedium = ValueUnion.psz; + else + hrc = E_FAIL; + break; + } + + case 't': // type + { + 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(Storage::tr("Invalid --type argument '%s'"), ValueUnion.psz); + } + else + hrc = E_FAIL; + break; + } + + case 'h': // passthrough + { + if (ValueUnion.psz) + pszPassThrough = ValueUnion.psz; + else + hrc = E_FAIL; + break; + } + + case 'e': // tempeject + { + if (ValueUnion.psz) + pszTempEject = ValueUnion.psz; + else + hrc = E_FAIL; + break; + } + + case 'n': // nonrotational + { + if (ValueUnion.psz) + pszNonRotational = ValueUnion.psz; + else + hrc = E_FAIL; + break; + } + + case 'u': // discard + { + if (ValueUnion.psz) + pszDiscard = ValueUnion.psz; + else + hrc = E_FAIL; + break; + } + + case 'o': // hotpluggable + { + if (ValueUnion.psz) + pszHotPluggable = ValueUnion.psz; + else + hrc = E_FAIL; + break; + } + + case 'b': // bandwidthgroup + { + if (ValueUnion.psz) + pszBandwidthGroup = ValueUnion.psz; + else + hrc = E_FAIL; + break; + } + + case 'f': // force unmount medium during runtime + { + fForceUnmount = true; + break; + } + + case 'C': + if (ValueUnion.psz) + bstrComment = ValueUnion.psz; + else + hrc = E_FAIL; + break; + + case 'q': + if (ValueUnion.psz) + { + bstrNewUuid = ValueUnion.psz; + fSetNewUuid = true; + } + else + hrc = E_FAIL; + break; + + case 'Q': + if (ValueUnion.psz) + { + bstrNewParentUuid = ValueUnion.psz; + fSetNewParentUuid = true; + } + else + hrc = 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) + hrc = 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(Storage::tr("Invalid medium type '%s'"), ValueUnion.psz); + fSetMediumType = true; + break; + } + + case 'I': // --intnet + fIntNet = true; + break; + + default: + { + errorGetOpt(c, &ValueUnion); + hrc = E_FAIL; + break; + } + } + } + + if (FAILED(hrc)) + return RTEXITCODE_FAILURE; + + if (!pszCtl) + return errorSyntax(Storage::tr("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(Storage::tr("Drive passthrough state cannot be changed while the VM is running\n")); + else if (pszBandwidthGroup) + throw Utf8Str(Storage::tr("Bandwidth group cannot be changed while the VM is running\n")); + } + + /* check if the storage controller is present */ + hrc = machine->GetStorageControllerByName(Bstr(pszCtl).raw(), + storageCtl.asOutParam()); + if (FAILED(hrc)) + throw Utf8StrFmt(Storage::tr("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(Storage::tr("Port not specified")); + } + if (device == ~0U) + { + if (maxDevices == 1) + device = 0; + else + return errorSyntax(Storage::tr("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 mediumAttachment; + DeviceType_T deviceType = DeviceType_Null; + hrc = machine->GetMediumAttachment(Bstr(pszCtl).raw(), port, device, + mediumAttachment.asOutParam()); + if (SUCCEEDED(hrc)) + { + 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(hrc) + || !( deviceType == DeviceType_DVD + || deviceType == DeviceType_Floppy) + ) + throw Utf8StrFmt(Storage::tr("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 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(Storage::tr("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 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 mediumAttachment; + hrc = machine->GetMediumAttachment(Bstr(pszCtl).raw(), port, + device, + mediumAttachment.asOutParam()); + if (SUCCEEDED(hrc)) + { + DeviceType_T deviceType; + mediumAttachment->COMGETTER(Type)(&deviceType); + + if (pszMedium) + { + if (!RTStrICmp(pszMedium, "additions")) + { + ComPtr pProperties; + CHECK_ERROR(a->virtualBox, + COMGETTER(SystemProperties)(pProperties.asOutParam())); + CHECK_ERROR(pProperties, COMGETTER(DefaultAdditionsISO)(bstrIso.asOutParam())); + strIso = Utf8Str(bstrIso); + if (strIso.isEmpty()) + throw Utf8Str(Storage::tr("Cannot find the Guest Additions ISO image\n")); + pszMedium = strIso.c_str(); + if (devTypeRequested == DeviceType_Null) + devTypeRequested = DeviceType_DVD; + } + ComPtr pExistingMedium; + hrc = openMedium(a, pszMedium, deviceType, + AccessMode_ReadWrite, + pExistingMedium, + false /* fForceNewUuidOnOpen */, + true /* fSilent */); + if (SUCCEEDED(hrc) && pExistingMedium) + { + if ( (deviceType == DeviceType_DVD) + || (deviceType == DeviceType_HardDisk) + ) + devTypeRequested = deviceType; + } + } + else + devTypeRequested = deviceType; + } + } + } + + if (devTypeRequested == DeviceType_Null) // still the initializer value? + throw Utf8Str(Storage::tr("Argument --type must be specified\n")); + + /* check if the device type is supported by the controller */ + { + com::SafeArray saDeviceTypes; + + CHECK_ERROR(systemProperties, GetDeviceTypesForStorageBus(storageBus, ComSafeArrayAsOutParam(saDeviceTypes))); + if (SUCCEEDED(hrc)) + { + ULONG driveCheck = 0; + for (size_t i = 0; i < saDeviceTypes.size(); ++ i) + if (saDeviceTypes[i] == devTypeRequested) + driveCheck++; + if (!driveCheck) + throw Utf8StrFmt(Storage::tr("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 host; + CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam())); + + if (devTypeRequested == DeviceType_DVD) + { + hrc = 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(Storage::tr("Invalid host DVD drive name \"%s\""), pszMedium + 5); + hrc = host->FindHostDVDDrive(Bstr(szPathReal).raw(), + pMedium2Mount.asOutParam()); + if (!pMedium2Mount) + throw Utf8StrFmt(Storage::tr("Invalid host DVD drive name \"%s\""), pszMedium + 5); + } + } + else + { + // floppy + hrc = host->FindHostFloppyDrive(Bstr(pszMedium + 5).raw(), + pMedium2Mount.asOutParam()); + if (!pMedium2Mount) + throw Utf8StrFmt(Storage::tr("Invalid host floppy drive name \"%s\""), pszMedium + 5); + } + } + else if (!RTStrICmp(pszMedium, "iSCSI")) + { + /* check for required options */ + if (bstrServer.isEmpty() || bstrTarget.isEmpty()) + throw Utf8StrFmt(Storage::tr("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(hrc)) goto leave; /** @todo r=andy Argh!! Why not using exceptions here? */ + if (!bstrPort.isEmpty()) + bstrServer = BstrFmt("%ls:%ls", bstrServer.raw(), bstrPort.raw()); + + // set the other iSCSI parameters as properties + com::SafeArray names; + com::SafeArray 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(hrc)) goto leave; + Bstr guid; + CHECK_ERROR(pMedium2Mount, COMGETTER(Id)(guid.asOutParam())); + if (FAILED(hrc)) goto leave; + RTPrintf(Storage::tr("iSCSI disk created. UUID: %s\n"), Utf8Str(guid).c_str()); + } + else + { + if (!pszMedium) + { + ComPtr mediumAttachment; + hrc = machine->GetMediumAttachment(Bstr(pszCtl).raw(), port, + device, + mediumAttachment.asOutParam()); + if (FAILED(hrc)) + throw Utf8Str(Storage::tr("Missing --medium argument")); + } + else + { + Bstr bstrMedium(pszMedium); + hrc = openMedium(a, pszMedium, devTypeRequested, + AccessMode_ReadWrite, pMedium2Mount, + fSetNewUuid, false /* fSilent */); + if (FAILED(hrc) || !pMedium2Mount) + throw Utf8StrFmt(Storage::tr("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(hrc)) + throw Utf8Str(Storage::tr("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(hrc)) + { + if (enmMediumTypeOld != enmMediumType) + { + CHECK_ERROR(pMedium2Mount, COMSETTER(Type)(enmMediumType)); + if (FAILED(hrc)) + throw Utf8Str(Storage::tr("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 mediumAttachment; + // check if there is a dvd/floppy drive at the given location, if not attach one first + hrc = machine->GetMediumAttachment(Bstr(pszCtl).raw(), + port, + device, + mediumAttachment.asOutParam()); + if (SUCCEEDED(hrc)) + { + DeviceType_T deviceType; + mediumAttachment->COMGETTER(Type)(&deviceType); + if (deviceType != devTypeRequested) + { + machine->DetachDevice(Bstr(pszCtl).raw(), port, device); + hrc = machine->AttachDeviceWithoutMedium(Bstr(pszCtl).raw(), + port, + device, + devTypeRequested); // DeviceType_DVD or DeviceType_Floppy + } + } + else + { + hrc = 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(hrc))) + { + ComPtr mattach; + CHECK_ERROR(machine, GetMediumAttachment(Bstr(pszCtl).raw(), port, + device, mattach.asOutParam())); + + if (SUCCEEDED(hrc)) + { + 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(Storage::tr("Invalid --passthrough argument '%s'"), pszPassThrough); + } + else + throw Utf8StrFmt(Storage::tr("Couldn't find the controller attachment for the controller '%s'\n"), pszCtl); + } + + if ( pszTempEject + && (SUCCEEDED(hrc))) + { + ComPtr mattach; + CHECK_ERROR(machine, GetMediumAttachment(Bstr(pszCtl).raw(), port, + device, mattach.asOutParam())); + + if (SUCCEEDED(hrc)) + { + 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(Storage::tr("Invalid --tempeject argument '%s'"), pszTempEject); + } + else + throw Utf8StrFmt(Storage::tr("Couldn't find the controller attachment for the controller '%s'\n"), pszCtl); + } + + if ( pszNonRotational + && (SUCCEEDED(hrc))) + { + ComPtr mattach; + CHECK_ERROR(machine, GetMediumAttachment(Bstr(pszCtl).raw(), port, + device, mattach.asOutParam())); + + if (SUCCEEDED(hrc)) + { + 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(Storage::tr("Invalid --nonrotational argument '%s'"), pszNonRotational); + } + else + throw Utf8StrFmt(Storage::tr("Couldn't find the controller attachment for the controller '%s'\n"), pszCtl); + } + + if ( pszDiscard + && (SUCCEEDED(hrc))) + { + ComPtr mattach; + CHECK_ERROR(machine, GetMediumAttachment(Bstr(pszCtl).raw(), port, + device, mattach.asOutParam())); + + if (SUCCEEDED(hrc)) + { + 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(Storage::tr("Invalid --discard argument '%s'"), pszDiscard); + } + else + throw Utf8StrFmt(Storage::tr("Couldn't find the controller attachment for the controller '%s'\n"), pszCtl); + } + + if ( pszHotPluggable + && (SUCCEEDED(hrc))) + { + ComPtr mattach; + CHECK_ERROR(machine, GetMediumAttachment(Bstr(pszCtl).raw(), port, + device, mattach.asOutParam())); + + if (SUCCEEDED(hrc)) + { + 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(Storage::tr("Invalid --hotpluggable argument '%s'"), pszHotPluggable); + } + else + throw Utf8StrFmt(Storage::tr("Couldn't find the controller attachment for the controller '%s'\n"), pszCtl); + } + + if ( pszBandwidthGroup + && !fRunTime + && SUCCEEDED(hrc)) + { + + if (!RTStrICmp(pszBandwidthGroup, "none")) + { + /* Just remove the bandwidth gorup. */ + CHECK_ERROR(machine, SetNoBandwidthGroupForDevice(Bstr(pszCtl).raw(), + port, device)); + } + else + { + ComPtr bwCtrl; + ComPtr bwGroup; + + CHECK_ERROR(machine, COMGETTER(BandwidthControl)(bwCtrl.asOutParam())); + + if (SUCCEEDED(hrc)) + { + CHECK_ERROR(bwCtrl, GetBandwidthGroup(Bstr(pszBandwidthGroup).raw(), bwGroup.asOutParam())); + if (SUCCEEDED(hrc)) + { + CHECK_ERROR(machine, SetBandwidthGroupForDevice(Bstr(pszCtl).raw(), + port, device, bwGroup)); + } + } + } + } + + /* commit changes */ + if (SUCCEEDED(hrc)) + CHECK_ERROR(machine, SaveSettings()); + } + catch (const Utf8Str &strError) + { + errorArgument("%s", strError.c_str()); + hrc = E_FAIL; + } + + // machine must always be unlocked, even on errors +leave: + a->session->UnlockMachine(); + + return SUCCEEDED(hrc) ? 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 machine; + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetState; + + if (a->argc < 4) + return errorSyntax(Storage::tr("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 + Assert(ValueUnion.psz); + pszBusType = ValueUnion.psz; + break; + + case 'c': // controller + 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(c, &ValueUnion); + } + } + + HRESULT hrc; + + /* 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(Storage::tr("Storage controller name not specified\n")); + } + + if (fRemoveCtl) + { + CHECK_ERROR(machine, RemoveStorageController(Bstr(pszCtl).raw())); + } + else + { + if (pszBusType) + { + ComPtr 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 if (!RTStrICmp(pszBusType, "virtio-scsi") || !RTStrICmp(pszBusType, "virtio")) + { + CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(), + StorageBus_VirtioSCSI, + ctl.asOutParam())); + } + else + { + errorArgument(Storage::tr("Invalid --add argument '%s'"), pszBusType); + hrc = E_FAIL; + } + } + + if ( pszCtlType + && SUCCEEDED(hrc)) + { + ComPtr ctl; + + CHECK_ERROR(machine, GetStorageControllerByName(Bstr(pszCtl).raw(), + ctl.asOutParam())); + + if (SUCCEEDED(hrc)) + { + 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 if (!RTStrICmp(pszCtlType, "virtio-scsi") || !RTStrICmp(pszCtlType, "virtio")) + { + CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_VirtioSCSI)); + } + else + { + errorArgument(Storage::tr("Invalid --type argument '%s'"), pszCtlType); + hrc = E_FAIL; + } + } + else + { + errorArgument(Storage::tr("Couldn't find the controller with the name: '%s'\n"), pszCtl); + hrc = E_FAIL; + } + } + + if ( (portcount != ~0U) + && SUCCEEDED(hrc)) + { + ComPtr ctl; + + CHECK_ERROR(machine, GetStorageControllerByName(Bstr(pszCtl).raw(), + ctl.asOutParam())); + + if (SUCCEEDED(hrc)) + { + CHECK_ERROR(ctl, COMSETTER(PortCount)(portcount)); + } + else + { + errorArgument(Storage::tr("Couldn't find the controller with the name: '%s'\n"), pszCtl); + hrc = E_FAIL; /** @todo r=andy Overwrites original hrc. */ + } + } + + if ( pszHostIOCache + && SUCCEEDED(hrc)) + { + ComPtr ctl; + + CHECK_ERROR(machine, GetStorageControllerByName(Bstr(pszCtl).raw(), + ctl.asOutParam())); + + if (SUCCEEDED(hrc)) + { + if (!RTStrICmp(pszHostIOCache, "on")) + { + CHECK_ERROR(ctl, COMSETTER(UseHostIOCache)(TRUE)); + } + else if (!RTStrICmp(pszHostIOCache, "off")) + { + CHECK_ERROR(ctl, COMSETTER(UseHostIOCache)(FALSE)); + } + else + { + errorArgument(Storage::tr("Invalid --hostiocache argument '%s'"), pszHostIOCache); + hrc = E_FAIL; + } + } + else + { + errorArgument(Storage::tr("Couldn't find the controller with the name: '%s'\n"), pszCtl); + hrc = E_FAIL; /** @todo r=andy Ditto. */ + } + } + + if ( pszBootable + && SUCCEEDED(hrc)) + { + if (SUCCEEDED(hrc)) + { + 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(Storage::tr("Invalid --bootable argument '%s'"), pszBootable); + hrc = E_FAIL; + } + } + else + { + errorArgument(Storage::tr("Couldn't find the controller with the name: '%s'\n"), pszCtl); + hrc = E_FAIL; + } + } + + if ( pszCtlNewName + && SUCCEEDED(hrc)) + { + ComPtr ctl; + + CHECK_ERROR(machine, GetStorageControllerByName(Bstr(pszCtl).raw(), + ctl.asOutParam())); + + if (SUCCEEDED(hrc)) + { + CHECK_ERROR(ctl, COMSETTER(Name)(Bstr(pszCtlNewName).raw())); + } + else + { + errorArgument(Storage::tr("Couldn't find the controller with the name: '%s'\n"), pszCtl); + hrc = E_FAIL; + } + } + + } + + /* commit changes */ + if (SUCCEEDED(hrc)) + CHECK_ERROR(machine, SaveSettings()); + + /* it's important to always close sessions */ + a->session->UnlockMachine(); + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageUSB.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageUSB.cpp new file mode 100644 index 00000000..f86a02c9 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageUSB.cpp @@ -0,0 +1,581 @@ +/* $Id: VBoxManageUSB.cpp $ */ +/** @file + * VBoxManage - VirtualBox's command-line interface. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "VBoxManage.h" + +#include + +using namespace com; + +DECLARE_TRANSLATION_CONTEXT(Usb); + +/** + * 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 +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 mActive; + Bstr mVendorId; + Bstr mProductId; + Bstr mRevision; + Bstr mManufacturer; + Bstr mProduct; + Bstr mPort; + Bstr mRemote; + Bstr mSerialNumber; + Nullable 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 mMachine; + USBFilter mFilter; +}; + +RTEXITCODE handleUSBFilter(HandlerArg *a) +{ + HRESULT hrc = S_OK; + USBFilterCmd cmd; + + /* which command? */ + cmd.mAction = USBFilterCmd::Invalid; + if (!strcmp(a->argv[0], "add")) + { + cmd.mAction = USBFilterCmd::Add; + setCurrentSubcommand(HELP_SCOPE_USBFILTER_ADD); + } + else if (!strcmp(a->argv[0], "modify")) + { + cmd.mAction = USBFilterCmd::Modify; + setCurrentSubcommand(HELP_SCOPE_USBFILTER_MODIFY); + } + else if (!strcmp(a->argv[0], "remove")) + { + cmd.mAction = USBFilterCmd::Remove; + setCurrentSubcommand(HELP_SCOPE_USBFILTER_REMOVE); + } + + if (cmd.mAction == USBFilterCmd::Invalid) + return errorUnknownSubcommand(a->argv[0]); + + /* which index? */ + if (VINF_SUCCESS != RTStrToUInt32Full(a->argv[1], 10, &cmd.mIndex)) + return errorSyntax(Usb::tr("Invalid index '%s'"), a->argv[1]); + + if (cmd.mAction == USBFilterCmd::Add || cmd.mAction == USBFilterCmd::Modify) + { + // 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; + + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + static const RTGETOPTDEF s_aOptions[] = + { + { "--target", 't', RTGETOPT_REQ_STRING }, + { "--name", 'n', RTGETOPT_REQ_STRING }, + { "--active", 'a', RTGETOPT_REQ_STRING }, + { "--vendorid", 'v', RTGETOPT_REQ_STRING }, + { "--productid", 'p', RTGETOPT_REQ_STRING }, + { "--revision", 'r', RTGETOPT_REQ_STRING }, + { "--manufacturer", 'm', RTGETOPT_REQ_STRING }, + { "--product", 'P', RTGETOPT_REQ_STRING }, + { "--serialnumber", 's', RTGETOPT_REQ_STRING }, + { "--port", 'o', RTGETOPT_REQ_STRING }, + { "--remote", 'R', RTGETOPT_REQ_STRING }, + { "--maskedinterfaces", 'M', RTGETOPT_REQ_UINT32 }, + { "--action", 'A', RTGETOPT_REQ_STRING } + }; + + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, 0 /*fFlags*/); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (vrc) + { + case 't': // --target + if (!strcmp(ValueUnion.psz, "global")) + cmd.mGlobal = true; + else + CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(ValueUnion.psz).raw(), + cmd.mMachine.asOutParam()), RTEXITCODE_FAILURE); + break; + case 'n': // --name + cmd.mFilter.mName = ValueUnion.psz; + break; + case 'a': // --active + if (!strcmp(ValueUnion.psz, "yes")) + cmd.mFilter.mActive = true; + else if (!strcmp(ValueUnion.psz, "no")) + cmd.mFilter.mActive = false; + else + return errorArgument(Usb::tr("Invalid --active argument '%s'"), ValueUnion.psz); + break; + case 'v': // --vendorid + cmd.mFilter.mVendorId = ValueUnion.psz; + break; + case 'p': // --productid + cmd.mFilter.mProductId = ValueUnion.psz; + break; + case 'r': // --revision + cmd.mFilter.mRevision = ValueUnion.psz; + break; + case 'm': // --manufacturer + cmd.mFilter.mManufacturer = ValueUnion.psz; + break; + case 'P': // --product + cmd.mFilter.mProduct = ValueUnion.psz; + break; + case 's': // --serialnumber + cmd.mFilter.mSerialNumber = ValueUnion.psz; + break; + case 'o': // --port + cmd.mFilter.mPort = ValueUnion.psz; + break; + case 'R': // --remote + cmd.mFilter.mRemote = ValueUnion.psz; + break; + case 'M': // --maskedinterfaces + cmd.mFilter.mMaskedInterfaces = ValueUnion.u32; + break; + case 'A': // --action + if (!strcmp(ValueUnion.psz, "ignore")) + cmd.mFilter.mAction = USBDeviceFilterAction_Ignore; + else if (!strcmp(ValueUnion.psz, "hold")) + cmd.mFilter.mAction = USBDeviceFilterAction_Hold; + else + return errorArgument(Usb::tr("Invalid USB filter action '%s'"), ValueUnion.psz); + break; + default: + return errorGetOpt(vrc, &ValueUnion); + } + } + + // mandatory/forbidden options + if (!cmd.mGlobal && !cmd.mMachine) + return errorSyntax(Usb::tr("Missing required option: --target")); + + if (cmd.mAction == USBFilterCmd::Add) + { + if (cmd.mFilter.mName.isEmpty()) + return errorSyntax(Usb::tr("Missing required option: --name")); + + if (cmd.mGlobal && cmd.mFilter.mAction == USBDeviceFilterAction_Null) + return errorSyntax(Usb::tr("Missing required option: --action")); + + if (cmd.mGlobal && !cmd.mFilter.mRemote.isEmpty()) + return errorSyntax(Usb::tr("Option --remote applies to VM filters only (--target=)")); + } + } + else if (cmd.mAction == USBFilterCmd::Remove) + { + RTGETOPTSTATE GetState; + RTGETOPTUNION ValueUnion; + static const RTGETOPTDEF s_aOptions[] = + { + { "--target", 't', RTGETOPT_REQ_STRING } + }; + int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, 0 /*fFlags*/); + AssertRCReturn(vrc, RTEXITCODE_FAILURE); + + while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (vrc) + { + case 't': // --target + if (!strcmp(ValueUnion.psz, "global")) + cmd.mGlobal = true; + else + CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(ValueUnion.psz).raw(), + cmd.mMachine.asOutParam()), RTEXITCODE_FAILURE); + break; + default: + return errorGetOpt(vrc, &ValueUnion); + } + } + // mandatory options + if (!cmd.mGlobal && !cmd.mMachine) + return errorSyntax(Usb::tr("Missing required option: --target")); + } + + USBFilterCmd::USBFilter &f = cmd.mFilter; + + ComPtr host; + ComPtr 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 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.mProduct.isEmpty()) + CHECK_ERROR_BREAK(flt, COMSETTER(Product)(f.mProduct.raw())); + if (!f.mPort.isEmpty()) + CHECK_ERROR_BREAK(flt, COMSETTER(Port)(f.mPort.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 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.mProduct.isEmpty()) + CHECK_ERROR_BREAK(flt, COMSETTER(Product)(f.mProduct.raw())); + if (!f.mPort.isEmpty()) + CHECK_ERROR_BREAK(flt, COMSETTER(Port)(f.mPort.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 coll; + CHECK_ERROR_BREAK(host, COMGETTER(USBDeviceFilters)(ComSafeArrayAsOutParam(coll))); + + ComPtr 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.mProduct.isEmpty()) + CHECK_ERROR_BREAK(flt, COMSETTER(Product)(f.mProduct.raw())); + if (!f.mPort.isEmpty()) + CHECK_ERROR_BREAK(flt, COMSETTER(Port)(f.mPort.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 coll; + CHECK_ERROR_BREAK(flts, COMGETTER(DeviceFilters)(ComSafeArrayAsOutParam(coll))); + + ComPtr 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.mProduct.isEmpty()) + CHECK_ERROR_BREAK(flt, COMSETTER(Product)(f.mProduct.raw())); + if (!f.mPort.isEmpty()) + CHECK_ERROR_BREAK(flt, COMSETTER(Port)(f.mPort.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 flt; + CHECK_ERROR_BREAK(host, RemoveUSBDeviceFilter(cmd.mIndex)); + } + else + { + ComPtr flt; + CHECK_ERROR_BREAK(flts, RemoveDeviceFilter(cmd.mIndex, flt.asOutParam())); + } + break; + } + default: + break; + } + + if (cmd.mMachine) + { + if (SUCCEEDED(hrc)) + { + /* commit the session */ + CHECK_ERROR(cmd.mMachine, SaveSettings()); + } + /* close the session */ + a->session->UnlockMachine(); + } + + return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +RTEXITCODE handleUSBDevSource(HandlerArg *a) +{ + HRESULT hrc = S_OK; + + /* at least: 0: command, 1: source id */ + if (a->argc < 2) + return errorSyntax(Usb::tr("Not enough parameters")); + + ComPtr host; + if (!strcmp(a->argv[0], "add")) + { + setCurrentSubcommand(HELP_SCOPE_USBDEVSOURCE_ADD); + + Bstr strBackend; + Bstr strAddress; + if (a->argc != 6) + return errorSyntax(Usb::tr("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(Usb::tr("Parameter \"%s\" is invalid"), a->argv[i]); + } + + SafeArray usbSourcePropNames; + SafeArray 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")) + { + setCurrentSubcommand(HELP_SCOPE_USBDEVSOURCE_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(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +/* vi: set tabstop=4 shiftwidth=4 expandtab: */ diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageUpdateCheck.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageUpdateCheck.cpp new file mode 100644 index 00000000..5b66b70b --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageUpdateCheck.cpp @@ -0,0 +1,392 @@ +/* $Id: VBoxManageUpdateCheck.cpp $ */ +/** @file + * VBoxManage - The 'updatecheck' command. + */ + +/* + * Copyright (C) 2020-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "VBoxManage.h" + +DECLARE_TRANSLATION_CONTEXT(UpdateCheck); + +using namespace com; // SafeArray + + +static RTEXITCODE doUpdateList(int argc, char **argv, ComPtr pUpdateAgent) +{ + /* + * Parse options. + */ + static const RTGETOPTDEF s_aOptions[] = + { + { "--machine-readable", 'm', RTGETOPT_REQ_NOTHING } + }; + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0 /* First */, 0); + AssertRCReturn(vrc, RTEXITCODE_INIT); + + bool fMachineReadable = false; + + int c; + RTGETOPTUNION ValueUnion; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'm': + fMachineReadable = true; + break; + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + /* + * Do the work. + */ + BOOL fEnabled; + CHECK_ERROR2I_RET(pUpdateAgent, COMGETTER(Enabled)(&fEnabled), RTEXITCODE_FAILURE); + if (fMachineReadable) + outputMachineReadableBool("enabled", &fEnabled); + else + RTPrintf(UpdateCheck::tr("Enabled: %s\n"), + fEnabled ? UpdateCheck::tr("yes") : UpdateCheck::tr("no")); + + ULONG cCheckCount; + CHECK_ERROR2I_RET(pUpdateAgent, COMGETTER(CheckCount)(&cCheckCount), RTEXITCODE_FAILURE); + if (fMachineReadable) + outputMachineReadableULong("count", &cCheckCount); + else + RTPrintf(UpdateCheck::tr("Count: %u\n"), cCheckCount); + + ULONG uCheckFreqSeconds; + CHECK_ERROR2I_RET(pUpdateAgent, COMGETTER(CheckFrequency)(&uCheckFreqSeconds), RTEXITCODE_FAILURE); + + ULONG uCheckFreqDays = uCheckFreqSeconds / RT_SEC_1DAY; + + if (fMachineReadable) + outputMachineReadableULong("frequency-days", &uCheckFreqDays); + else if (uCheckFreqDays == 0) + RTPrintf(UpdateCheck::tr("Frequency: Never\n")); + else if (uCheckFreqDays == 1) + RTPrintf(UpdateCheck::tr("Frequency: Every day\n")); + else + RTPrintf(UpdateCheck::tr("Frequency: Every %u days\n"), uCheckFreqDays); + + UpdateChannel_T enmUpdateChannel; + CHECK_ERROR2I_RET(pUpdateAgent, COMGETTER(Channel)(&enmUpdateChannel), RTEXITCODE_FAILURE); + const char *psz; + const char *pszMachine; + switch (enmUpdateChannel) + { + case UpdateChannel_Stable: + psz = UpdateCheck::tr("Stable - new minor and maintenance releases"); + pszMachine = "stable"; + break; + case UpdateChannel_All: + psz = UpdateCheck::tr("All releases - new minor, maintenance, and major releases"); + pszMachine = "all-releases"; + break; + case UpdateChannel_WithBetas: + psz = UpdateCheck::tr("With Betas - new minor, maintenance, major, and beta releases"); + pszMachine = "with-betas"; + break; + default: + AssertFailed(); + psz = UpdateCheck::tr("Unset"); + pszMachine = "invalid"; + break; + } + if (fMachineReadable) + outputMachineReadableString("channel", pszMachine); + else + RTPrintf(UpdateCheck::tr("Channel: %s\n"), psz); + + Bstr bstrVal; + CHECK_ERROR2I_RET(pUpdateAgent, COMGETTER(LastCheckDate)(bstrVal.asOutParam()), + RTEXITCODE_FAILURE); + if (fMachineReadable) + outputMachineReadableString("last-check-date", &bstrVal); + else if (bstrVal.isNotEmpty()) + RTPrintf(UpdateCheck::tr("Last Check Date: %ls\n"), bstrVal.raw()); + + CHECK_ERROR2I_RET(pUpdateAgent, COMGETTER(RepositoryURL)(bstrVal.asOutParam()), RTEXITCODE_FAILURE); + if (fMachineReadable) + outputMachineReadableString("repo-url", &bstrVal); + else + RTPrintf(UpdateCheck::tr("Repository: %ls\n"), bstrVal.raw()); + + return RTEXITCODE_SUCCESS; +} + +static RTEXITCODE doUpdateModify(int argc, char **argv, ComPtr pUpdateAgent) +{ + /* + * Parse options. + */ + static const RTGETOPTDEF s_aOptions[] = + { + { "--enable", 'e', RTGETOPT_REQ_NOTHING }, + { "--disable", 'd', RTGETOPT_REQ_NOTHING }, + { "--channel", 'c', RTGETOPT_REQ_STRING }, + { "--frequency", 'f', RTGETOPT_REQ_UINT32 }, + }; + + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0 /* First */, 0); + AssertRCReturn(vrc, RTEXITCODE_INIT); + + int fEnabled = -1; /* Tristate: -1 (not modified), false, true. */ + UpdateChannel_T enmChannel = (UpdateChannel_T)-1; + uint32_t cFrequencyDays = 0; + + int c; + RTGETOPTUNION ValueUnion; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'e': + fEnabled = true; + break; + + case 'd': + fEnabled = false; + break; + + case 'c': + if (!RTStrICmp(ValueUnion.psz, "stable")) + enmChannel = UpdateChannel_Stable; + else if (!RTStrICmp(ValueUnion.psz, "withbetas")) + enmChannel = UpdateChannel_WithBetas; + /** @todo UpdateChannel_WithTesting once supported. */ + else if (!RTStrICmp(ValueUnion.psz, "all")) + enmChannel = UpdateChannel_All; + else + return errorArgument(UpdateCheck::tr("Invalid channel specified: '%s'"), ValueUnion.psz); + break; + + case 'f': + cFrequencyDays = ValueUnion.u32; + if (cFrequencyDays == 0) + return errorArgument(UpdateCheck::tr("The update frequency cannot be zero")); + break; + + /** @todo Add more options like repo handling etc. */ + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + if ( fEnabled == -1 + && enmChannel == (UpdateChannel_T)-1 + && cFrequencyDays == 0) + return errorSyntax(UpdateCheck::tr("No change requested")); + + /* + * Make the changes. + */ + if (enmChannel != (UpdateChannel_T)-1) + { + CHECK_ERROR2I_RET(pUpdateAgent, COMSETTER(Channel)(enmChannel), RTEXITCODE_FAILURE); + } + if (fEnabled != -1) + { + CHECK_ERROR2I_RET(pUpdateAgent, COMSETTER(Enabled)((BOOL)fEnabled), RTEXITCODE_FAILURE); + } + if (cFrequencyDays) + { + CHECK_ERROR2I_RET(pUpdateAgent, COMSETTER(CheckFrequency)(cFrequencyDays * RT_SEC_1DAY), RTEXITCODE_FAILURE); + } + return RTEXITCODE_SUCCESS; +} + +static RTEXITCODE doUpdateCheck(int argc, char **argv, ComPtr pUpdateAgent) +{ + /* + * Parse arguments. + */ + static const RTGETOPTDEF s_aOptions[] = + { + { "--machine-readable", 'm', RTGETOPT_REQ_NOTHING } + }; + RTGETOPTSTATE GetState; + int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0 /* First */, 0); + AssertRCReturn(vrc, RTEXITCODE_INIT); + + bool fMachineReadable = false; + + int c; + RTGETOPTUNION ValueUnion; + while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0) + { + switch (c) + { + case 'm': + fMachineReadable = true; + break; + + default: + return errorGetOpt(c, &ValueUnion); + } + } + + /* + * Do the work. + */ + Bstr bstrName; + CHECK_ERROR2I_RET(pUpdateAgent, COMGETTER(Name)(bstrName.asOutParam()), RTEXITCODE_FAILURE); + + if (!fMachineReadable) + RTPrintf(UpdateCheck::tr("Checking for a new %ls version...\n"), bstrName.raw()); + + /* + * We don't call CHECK_ERROR2I_RET(pHostUpdate, VBoxUpdate(updateCheckType, ...); here so we can check for a specific + * return value indicating update checks are disabled. + */ + ComPtr pProgress; + HRESULT hrc = pUpdateAgent->CheckFor(pProgress.asOutParam()); + if (FAILED(hrc)) + { + if (pProgress.isNull()) + RTStrmPrintf(g_pStdErr, UpdateCheck::tr("Failed to create update progress object: %Rhrc\n"), hrc); + else + com::GlueHandleComError(pUpdateAgent, "HostUpdate(UpdateChannel_Stable, pProgress.asOutParam())", + hrc, __FILE__, __LINE__); + return RTEXITCODE_FAILURE; + } + + /* HRESULT hrc = */ showProgress(pProgress, fMachineReadable ? SHOW_PROGRESS_NONE : SHOW_PROGRESS); + CHECK_PROGRESS_ERROR_RET(pProgress, (UpdateCheck::tr("Checking for update failed.")), RTEXITCODE_FAILURE); + + UpdateState_T updateState; + CHECK_ERROR2I_RET(pUpdateAgent, COMGETTER(State)(&updateState), RTEXITCODE_FAILURE); + + BOOL const fUpdateNeeded = updateState == UpdateState_Available; + if (fMachineReadable) + outputMachineReadableBool("update-needed", &fUpdateNeeded); + + switch (updateState) + { + case UpdateState_Available: + { + Bstr bstrUpdateVersion; + CHECK_ERROR2I_RET(pUpdateAgent, COMGETTER(Version)(bstrUpdateVersion.asOutParam()), RTEXITCODE_FAILURE); + Bstr bstrUpdateURL; + CHECK_ERROR2I_RET(pUpdateAgent, COMGETTER(DownloadUrl)(bstrUpdateURL.asOutParam()), RTEXITCODE_FAILURE); + + if (!fMachineReadable) + RTPrintf(UpdateCheck::tr( + "A new version of %ls has been released! Version %ls is available at virtualbox.org.\n" + "You can download this version here: %ls\n"), + bstrName.raw(), bstrUpdateVersion.raw(), bstrUpdateURL.raw()); + else + { + outputMachineReadableString("update-version", &bstrUpdateVersion); + outputMachineReadableString("update-url", &bstrUpdateURL); + } + + break; + } + + case UpdateState_NotAvailable: + { + if (!fMachineReadable) + RTPrintf(UpdateCheck::tr("You are already running the most recent version of %ls.\n"), bstrName.raw()); + break; + } + + case UpdateState_Canceled: + break; + + case UpdateState_Error: + RT_FALL_THROUGH(); + default: + { + if (!fMachineReadable) + RTPrintf(UpdateCheck::tr("Something went wrong while checking for updates!\n" + "Please check network connection and try again later.\n")); + break; + } + } + + return RTEXITCODE_SUCCESS; +} + +/** + * Handles the 'updatecheck' command. + * + * @returns Appropriate exit code. + * @param a Handler argument. + */ +RTEXITCODE handleUpdateCheck(HandlerArg *a) +{ + ComPtr pHost; + CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(Host)(pHost.asOutParam()), RTEXITCODE_FAILURE); + + ComPtr pUpdate; + CHECK_ERROR2I_RET(pHost, COMGETTER(UpdateHost)(pUpdate.asOutParam()), RTEXITCODE_FAILURE); + /** @todo Add other update agents here. */ + + if (a->argc < 1) + return errorNoSubcommand(); + if (!RTStrICmp(a->argv[0], "perform")) + { + setCurrentSubcommand(HELP_SCOPE_UPDATECHECK_PERFORM); + return doUpdateCheck(a->argc - 1, &a->argv[1], pUpdate); + } + if (!RTStrICmp(a->argv[0], "list")) + { + setCurrentSubcommand(HELP_SCOPE_UPDATECHECK_LIST); + return doUpdateList(a->argc - 1, &a->argv[1], pUpdate); + } + if (!RTStrICmp(a->argv[0], "modify")) + { + setCurrentSubcommand(HELP_SCOPE_UPDATECHECK_MODIFY); + return doUpdateModify(a->argc - 1, &a->argv[1], pUpdate); + } + return errorUnknownSubcommand(a->argv[0]); +} + +/* vi: set tabstop=4 shiftwidth=4 expandtab: */ diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageUtils.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageUtils.cpp new file mode 100644 index 00000000..ad3fc508 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageUtils.cpp @@ -0,0 +1,131 @@ +/* $Id: VBoxManageUtils.cpp $ */ +/** @file + * VBoxManageUtils.h - VBoxManage utility functions. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "VBoxManageUtils.h" +#include "VBoxManage.h" + +#include +#include + +#include +#include +#include + +using namespace com; + +DECLARE_TRANSLATION_CONTEXT(Utils); + +unsigned int getMaxNics(const ComPtr &pVirtualBox, + const ComPtr &pMachine) +{ + ULONG NetworkAdapterCount = 0; + do { + HRESULT hrc; + + ComPtr info; + CHECK_ERROR_BREAK(pVirtualBox, COMGETTER(SystemProperties)(info.asOutParam())); + + ChipsetType_T aChipset; + CHECK_ERROR_BREAK(pMachine, COMGETTER(ChipsetType)(&aChipset)); + + CHECK_ERROR_BREAK(info, GetMaxNetworkAdapters(aChipset, &NetworkAdapterCount)); + } while (0); + + return (unsigned int)NetworkAdapterCount; +} + + +/** + * API does NOT verify that whether the interface name set as the + * bridged or host-only interface of a NIC is valid. Warn the user if + * IHost doesn't seem to know about it (non-fatal). + */ +void verifyHostNetworkInterfaceName(const ComPtr &pVirtualBox, + const char *pszTargetName, + HostNetworkInterfaceType_T enmTargetType) +{ + HRESULT hrc; + + AssertReturnVoid( enmTargetType == HostNetworkInterfaceType_Bridged + || enmTargetType == HostNetworkInterfaceType_HostOnly); + + ComPtr host; + hrc = pVirtualBox->COMGETTER(Host)(host.asOutParam()); + if (FAILED(hrc)) + return; + + SafeIfaceArray ifs; + hrc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(ifs)); + if (FAILED(hrc)) + return; + + for (size_t i = 0; i < ifs.size(); ++i) + { + const ComPtr iface = ifs[i]; + + Bstr bstrName; + hrc = iface->COMGETTER(Name)(bstrName.asOutParam()); + if (FAILED(hrc)) + return; + + if (!bstrName.equals(pszTargetName)) + continue; + + /* we found the interface but is it the right type? */ + HostNetworkInterfaceType_T enmType; + hrc = iface->COMGETTER(InterfaceType)(&enmType); + if (FAILED(hrc)) + return; + + if (enmType == enmTargetType) + return; /* seems ok */ + + const char *pszTypeName; + char a_szUnknownTypeBuf[32]; + switch (enmType) + { + case HostNetworkInterfaceType_Bridged: + pszTypeName = Utils::tr("type bridged"); + break; + + case HostNetworkInterfaceType_HostOnly: + pszTypeName = Utils::tr("type host-only"); + break; + + default: + RTStrPrintf(a_szUnknownTypeBuf, sizeof(a_szUnknownTypeBuf), + Utils::tr("unknown type %RU32"), enmType); + pszTypeName = a_szUnknownTypeBuf; + break; + } + + RTMsgWarning(Utils::tr("Interface \"%s\" is of %s"), pszTargetName, pszTypeName); + return; + } + + RTMsgWarning(Utils::tr("Interface \"%s\" doesn't seem to exist"), pszTargetName); +} diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageUtils.h b/src/VBox/Frontends/VBoxManage/VBoxManageUtils.h new file mode 100644 index 00000000..55241c37 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageUtils.h @@ -0,0 +1,45 @@ +/* $Id: VBoxManageUtils.h $ */ +/** @file + * VBoxManageUtils.h - Declarations for VBoxManage utility functions. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#ifndef VBOX_INCLUDED_SRC_VBoxManage_VBoxManageUtils_h +#define VBOX_INCLUDED_SRC_VBoxManage_VBoxManageUtils_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include +#include +#include + +unsigned int getMaxNics(const ComPtr &pVirtualBox, + const ComPtr &pMachine); + +void verifyHostNetworkInterfaceName(const ComPtr &pVirtualBox, + const char *pszTargetName, + HostNetworkInterfaceType_T enmTargetType); + +#endif /* !VBOX_INCLUDED_SRC_VBoxManage_VBoxManageUtils_h */ diff --git a/src/VBox/Frontends/VBoxManage/nls/ApprovedLanguages.kmk b/src/VBox/Frontends/VBoxManage/nls/ApprovedLanguages.kmk new file mode 100644 index 00000000..de405a5d --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/nls/ApprovedLanguages.kmk @@ -0,0 +1,40 @@ +# $Id: ApprovedLanguages.kmk $ +## @file +# ApprovedLanguages.kmk - List of approved VBoxManage translations. +# + +# +# Copyright (C) 2007-2023 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# SPDX-License-Identifier: GPL-3.0-only +# + +# The list of approved VBoxManage languages. +VBOX_APPROVED_VBOXMANAGE_LANGUAGES := \ + ru + +# The list of approved VBoxManage language codes for built-in docbook +# help. Order matters for codes in the case of a language used in +# multiple countries. The language code used when no country is +# specified should be placed before other codes with the same language. +# For example: if en_US, en_UK and en_AU are approved the en_US should +# be placed before others allowing en_US to be used when the user +# specifies "en" as the desired language. +VBOX_APPROVED_VBOXMANAGE_DOCBOOK_LANGUAGES := \ + ru_RU diff --git a/src/VBox/Frontends/VBoxManage/nls/VBoxManageNls_ru.ts b/src/VBox/Frontends/VBoxManage/nls/VBoxManageNls_ru.ts new file mode 100644 index 00000000..6cf00e15 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/nls/VBoxManageNls_ru.ts @@ -0,0 +1,12354 @@ + + + + + Appliance + + + + Option "%s" can't be used together with "--cloud" option. + ŠžŠæцŠøя "%s" Š½Šµ Š¼Š¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š½Š° Š²Š¼ŠµŃŃ‚Šµ с "--cloud". + + + + + + + Value of option "%s" is out of range. + Š—Š½Š°Ń‡ŠµŠ½ŠøŠµ Š¾ŠæцŠøŠø "%s" Š²Ń‹Ń…Š¾Š“Šøт Š·Š° Š³Ń€Š°Š½Šøцы Š“ŠøŠ°ŠæŠ°Š·Š¾Š½Š°. + + + + + + + + + + Option "%s" requires preceding --vsys or --cloud option. + ŠžŠæцŠøя "%s" трŠµŠ±ŃƒŠµŃ‚ Š½Š°Š»ŠøчŠøя ŠæрŠµŠ“шŠµŃŃ‚Š²ŃƒŠµŃ‰ŠµŠ³Š¾ --vsys ŠøŠ»Šø --cloud. + + + + + + + + + + + + + + + + + + + + Option "%s" requires preceding --vsys option. + ŠžŠæцŠøя "%s" трŠµŠ±ŃƒŠµŃ‚ Š½Š°Š»ŠøчŠøя ŠæрŠµŠ“шŠµŃŃ‚Š²ŃƒŠµŃ‰ŠµŠ³Š¾ --vsys. + + + + + + + + Option "%s" requires preceding --unit option. + ŠžŠæцŠøя "%s" трŠµŠ±ŃƒŠµŃ‚ Š½Š°Š»ŠøчŠøя ŠæрŠµŠ“шŠµŃŃ‚Š²ŃƒŠµŃ‰ŠµŠ³Š¾ --unit. + + + + Invalid import options '%s' + + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Šµ Š¾ŠæцŠøŠø ŠøŠ¼ŠæŠ¾Ń€Ń‚Š° '%s' + + + + + + Option "%s" can't be used together with "--vsys" option. + ŠžŠæцŠøя "%s" Š½Šµ Š¼Š¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š½Š° Š²Š¼ŠµŃŃ‚Šµ с "--vsys". + + + + + + + + + + + + + + + + + + + Option "%s" requires preceding --cloud option. + ŠžŠæцŠøя "%s" трŠµŠ±ŃƒŠµŃ‚ Š½Š°Š»ŠøчŠøя ŠæрŠµŠ“шŠµŃŃ‚Š²ŃƒŠµŃ‰ŠµŠ³Š¾ --cloud. + + + + Invalid parameter '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ '%s' + + + + Invalid option -%c + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š°Ń Š¾ŠæцŠøя -%c + + + + Invalid option case %i + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š²Š°Ń€ŠøŠ°Š½Ń‚ Š¾ŠæцŠøŠø %i + + + + unknown option: %s + + ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Š°Ń Š¾ŠæцŠøя: %s + + + + + error: %Rrs + ŠžŃˆŠøŠ±ŠŗŠ°: %Rrs + + + + Not enough arguments for "import" command. + ŠŠµŠ“Š¾ŃŃ‚Š°Ń‚Š¾Ń‡Š½Š¾ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚Š¾Š² Š“Š»Ń ŠŗŠ¾Š¼Š°Š½Š“ы "import". + + + + Not enough arguments for import from the Cloud. + ŠŠµŠ“Š¾ŃŃ‚Š°Ń‚Š¾Ń‡Š½Š¾ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚Š¾Š² Š“Š»Ń ŠøŠ¼ŠæŠ¾Ń€Ń‚Š° ŠøŠ· Š¾Š±Š»Š°ŠŗŠ°. + + + + Appliance read failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾Ń‡ŠµŃŃ‚ŃŒ ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†Šøю + + + + Interpreting %ls... + + Š˜Š½Ń‚ŠµŃ€ŠæрŠµŃ‚Š°Ń†Šøя %ls... + + + + + Disks: + + Š”ŠøсŠŗŠø: + + + + + Invalid index %RI32 with -vsys option; the OVF contains only %zu virtual system(s). + + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠøŠ½Š“ŠµŠŗс %RI32 у Š¾ŠæцŠøŠøŠø -vsys; OVF сŠ¾Š“ŠµŃ€Š¶Šøт тŠ¾Š»ŃŒŠŗŠ¾ %zu Š²ŠøртуŠ°Š»ŃŒŠ½ŃƒŃŽ сŠøстŠµŠ¼Ńƒ. + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠøŠ½Š“ŠµŠŗс %RI32 у Š¾ŠæцŠøŠø -vsys; OVF сŠ¾Š“ŠµŃ€Š¶Šøт тŠ¾Š»ŃŒŠŗŠ¾ %zu Š²ŠøртуŠ°Š»ŃŒŠ½Ń‹Šµ сŠøстŠµŠ¼Ń‹. + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠøŠ½Š“ŠµŠŗс %RI32 у Š¾ŠæцŠøŠøŠø -vsys; OVF сŠ¾Š“ŠµŃ€Š¶Šøт тŠ¾Š»ŃŒŠŗŠ¾ %zu Š²ŠøртуŠ°Š»ŃŒŠ½Ń‹Ń… сŠøстŠµŠ¼. + + + + + Virtual system %u: + + Š’ŠøртуŠ°Š»ŃŒŠ½Š°Ń сŠøстŠµŠ¼Š° %u: + + + + + %2u: OS type specified with --ostype: "%ls" + + %2u: Š¢ŠøŠæ ŠžŠ” уŠŗŠ°Š·Š°Š½Š½Ń‹Š¹ чŠµŃ€ŠµŠ· --ostype: "%ls" + + + + + %2u: Suggested OS type: "%ls" + (change with "--vsys %u --ostype <type>"; use "list ostypes" to list all possible values) + + %2u: ŠŸŃ€ŠµŠ“Š»Š¾Š¶ŠµŠ½Š½Ń‹Š¹ тŠøŠæ ŠžŠ”: "%ls" + (ŠøŠ·Š¼ŠµŠ½Šøть чŠµŃ€ŠµŠ· "--vsys %u --ostype <тŠøŠæ>"; ŠøсŠæŠ¾Š»ŃŒŠ·ŃƒŠ¹Ń‚Šµ "list ostypes" Š“Š»Ń ŠæрŠ¾ŃŠ¼Š¾Ń‚Ń€Š° Š²ŃŠµŃ… Š²Š¾Š·Š¼Š¾Š¶Š½Ń‹Ń… Š·Š½Š°Ń‡ŠµŠ½ŠøŠ¹) + + + + + %2u: VM name specified with --vmname: "%ls" + + %2u: Š˜Š¼Ń Š’Šœ, уŠŗŠ°Š·Š°Š½Š½Š¾Šµ чŠµŃ€ŠµŠ· --vmname: "%ls" + + + + + %2u: Suggested VM name "%ls" + (change with "--vsys %u --vmname <name>") + + %2u: ŠŸŃ€ŠµŠ“Š»Š¾Š¶ŠµŠ½Š½Šµ ŠøŠ¼Ń Š’Šœ "%ls" + (ŠøŠ·Š¼ŠµŠ½Šøть чŠµŃ€ŠµŠ· "--vsys %u --vmname <ŠøŠ¼Ń>") + + + + + %2u: Product (ignored): %ls + + %2u: ŠŸŃ€Š¾Š“уŠŗт (ŠæрŠ¾ŠøŠ³Š½Š¾Ń€ŠøрŠ¾Š²Š°Š½): %ls + + + + + %2u: ProductUrl (ignored): %ls + + %2u: Url ŠæрŠ¾Š“уŠŗтŠ° (ŠæрŠ¾ŠøŠ³Š½Š¾Ń€ŠøрŠ¾Š²Š°Š½): %ls + + + + + %2u: Vendor (ignored): %ls + + %2u: ŠŸŠ¾ŃŃ‚Š°Š²Ń‰ŠøŠŗ (ŠæрŠ¾ŠøŠ³Š½Š¾Ń€ŠøрŠ¾Š²Š°Š½): %ls + + + + + %2u: VendorUrl (ignored): %ls + + %2u: Url ŠæŠ¾ŃŃ‚Š°Š²Ń‰ŠøŠŗŠ° (ŠæрŠ¾ŠøŠ³Š½Š¾Ń€ŠøрŠ¾Š²Š°Š½): %ls + + + + + %2u: Version (ignored): %ls + + %2u: Š’ŠµŃ€ŃŠøя (ŠæрŠ¾ŠøŠ³Š½Š¾Ń€ŠøрŠ¾Š²Š°Š½Š°): %ls + + + + + %2u: Description specified with --description: "%ls" + + %2u: ŠžŠæŠøсŠ°Š½ŠøŠµ, уŠŗŠ°Š·Š°Š½Š½Š¾Šµ чŠµŃ€ŠµŠ· --description: "%ls" + + + + + %2u: Description "%ls" + (change with "--vsys %u --description <desc>") + + %2u: ŠžŠæŠøсŠ°Š½ŠøŠµ "%ls" + (ŠøŠ·Š¼ŠµŠ½Šøть чŠµŃ€ŠµŠ· "--vsys %u --description <Š¾ŠæŠøсŠ°Š½ŠøŠµ>") + + + + + %2u: End-user license agreement + (accept with "--vsys %u --eula accept"): + +%ls + + + %2u: Š›ŠøцŠµŠ½Š·ŠøŠ¾Š½Š½Š¾Šµ сŠ¾Š³Š»Š°ŃˆŠµŠ½ŠøŠµ + (ŠæрŠøŠ½ŃŃ‚ŃŒ чŠµŃ€ŠµŠ· "--vsys %u --eula accept"): + +%ls + + + + + + %2u: End-user license agreement (accepted) + + %2u: Š›ŠøцŠµŠ½Š·ŠøŠ¾Š½Š½Š¾Šµ сŠ¾Š³Š»Š°ŃˆŠµŠ½ŠøŠµ (ŠæрŠøŠ½ŃŃ‚Š¾) + + + + + Argument to --eula must be either "show" or "accept". + ŠŃ€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --eula Š“Š¾Š»Š¶ŠµŠ½ Š±Ń‹Ń‚ŃŒ ŠøŠ»Šø "show" ŠøŠ»Šø "accept". + + + + %2u: End-user license agreement + (display with "--vsys %u --eula show"; + accept with "--vsys %u --eula accept") + + %2u: Š›ŠøцŠµŠ½Š·ŠøŠ¾Š½Š½Š¾Šµ сŠ¾Š³Š»Š°ŃˆŠµŠ½ŠøŠµ + (ŠæŠ¾ŠŗŠ°Š·Š°Ń‚ŃŒ чŠµŃ€ŠµŠ· "--vsys %u --eula show"; + ŠæрŠøŠ½ŃŃ‚ŃŒ чŠµŃ€ŠµŠ· "--vsys %u --eula accept") + + + + + %2u: No. of CPUs specified with --cpus: %ls + + %2u: Š§ŠøсŠ»Š¾ Š¦ŠŸŠ£ уŠŗŠ°Š·Š°Š½Š½Ń‹Ń… чŠµŃ€ŠµŠ· --cpus: %ls + + + + + Argument to --cpus option must be a number greater than %d and less than %d. + ŠŃ€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --cpus Š“Š¾Š¶ŠµŠ½ Š±Ń‹Ń‚ŃŒ чŠøсŠ»Š¾Š¼ Š±Š¾Š»ŃŒŃˆŠµ %d Šø Š¼ŠµŠ½ŃŒŃˆŠµ %d. + + + + %2u: Number of CPUs: %ls + (change with "--vsys %u --cpus <n>") + + %2u: Š§ŠøсŠ»Š¾ Š¦ŠŸŠ£: %ls + (ŠøŠ·Š¼ŠµŠ½Šøть чŠµŃ€ŠµŠ· "--vsys %u --cpus <n>") + + + + + %2u: Guest memory specified with --memory: %ls MB + + %2u: Š Š°Š·Š¼ŠµŃ€ ŠæŠ°Š¼ŃŃ‚Šø Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ сŠøстŠµŠ¼Ń‹, уŠŗŠ°Š·Š°Š½Š½Š¾Š¹ чŠµŃ€ŠµŠ· --memory: %ls MB + + + + + Argument to --memory option must be a non-negative number. + ŠŃ€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --memory Š“Š¾Š»Š¶ŠµŠ½ Š±Ń‹Ń‚ŃŒ Š½ŠµŠ¾Ń‚Ń€ŠøцŠ°Ń‚ŠµŠ»ŃŒŠ½Ń‹Š¼ чŠøсŠ»Š¾Š¼. + + + + %2u: Guest memory: %ls MB + (change with "--vsys %u --memory <MB>") + + %2u: Š Š°Š·Š¼ŠµŃ€ ŠæŠ°Š¼ŃŃ‚Šø Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ сŠøстŠµŠ¼Ń‹: %ls MB + (ŠøŠ·Š¼ŠµŠ½Šøть чŠµŃ€ŠµŠ· "--vsys %u --memory <MB>") + + + + + %2u: IDE controller, type %ls -- disabled + + %2u: IDE ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€, тŠøŠæ %ls -- Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½ + + + + + %2u: IDE controller, type %ls + (disable with "--vsys %u --unit %u --ignore") + + %2u: IDE ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€, тŠøŠæ %ls + (Š¾Ń‚ŠŗŠ»ŃŽŃ‡Šøть чŠµŃ€ŠµŠ· "--vsys %u --unit %u --ignore") + + + + + %2u: SATA controller, type %ls -- disabled + + %2u: SATA ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€, тŠøŠæ %ls -- Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½ + + + + + %2u: SATA controller, type %ls + (disable with "--vsys %u --unit %u --ignore") + + %2u: SATA ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€, тŠøŠæ %ls + (Š¾Ń‚ŠŗŠ»ŃŽŃ‡Šøть чŠµŃ€ŠµŠ· "--vsys %u --unit %u --ignore") + + + + + %2u: SAS controller, type %ls -- disabled + + %2u: SAS ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€, тŠøŠæ %ls -- Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½ + + + + + %2u: SAS controller, type %ls + (disable with "--vsys %u --unit %u --ignore") + + %2u: SAS ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€, тŠøŠæ %ls + (Š¾Ń‚ŠŗŠ»ŃŽŃ‡Šøть чŠµŃ€ŠµŠ· "--vsys %u --unit %u --ignore") + + + + + %2u: SCSI controller, type %ls -- disabled + + %2u: SCSI ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€, тŠøŠæ %ls -- Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½ + + + + + %2u: SCSI controller, type set with --unit %u --scsitype: "%ls" + + %2u: SCSI ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€, тŠøŠæ устŠ°Š½Š¾Š²Š»ŠµŠ½ чŠµŃ€ŠµŠ· --unit %u --scsitype: "%ls" + + + + + %2u: SCSI controller, type %ls + (change with "--vsys %u --unit %u --scsitype {BusLogic|LsiLogic}"; + disable with "--vsys %u --unit %u --ignore") + + %2u: SCSI ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€, тŠøŠæ %ls + (ŠøŠ·Š¼ŠµŠ½Šøть чŠµŃ€ŠµŠ· "--vsys %u --unit %u --scsitype {BusLogic|LsiLogic}"; + Š¾Ń‚ŠŗŠ»ŃŽŃ‡Šøть чŠµŃ€ŠµŠ· "--vsys %u --unit %u --ignore") + + + + + %2u: VirtioSCSI controller, type %ls -- disabled + + %2u: VirtioSCSI ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€, тŠøŠæ %ls -- Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½ + + + + + %2u: VirtioSCSI controller, type %ls + (disable with "--vsys %u --unit %u --ignore") + + %2u: VirtioSCSI ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€, тŠøŠæ %ls + (Š¾Ń‚ŠŗŠ»ŃŽŃ‡Šøть чŠµŃ€ŠµŠ· "--vsys %u --unit %u --ignore") + + + + + %2u: Hard disk image: source image=%ls -- disabled + + %2u: ŠžŠ±Ń€Š°Š· Š¶ŠµŃŃ‚ŠŗŠ¾Š³Š¾ Š“ŠøсŠŗŠ°: Š¾Š±Ń€Š°Š· ŠøстŠ¾Ń‡Š½ŠøŠŗŠ°=%ls -- Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½ + + + + Option --ImportToVDI shall not be used together with manually set target path. + ŠžŠæцŠøя --ImportToVDI Š½Šµ Š“Š¾Š»Š¶Š½Š° ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŃŒŃŃ Š²Š¼ŠµŃŃ‚Šµ с ŠæутŠµŠ¼ Š½Š°Š·Š½Š°Ń‡ŠµŠ½Šøя, уŠŗŠ°Š·Š°Š½Š½Ń‹Š¼ Š²Ń€ŃƒŃ‡Š½ŃƒŃŽ. + + + %2u: Hard disk image: source image=%ls, target path=%ls, %ls + + %2u: ŠžŠ±Ń€Š°Š· Š¶ŠµŃŃ‚ŠŗŠ¾Š³Š¾ Š“ŠøсŠŗŠ°: Š¾Š±Ń€Š°Š· ŠøстŠ¾Ń‡Š½ŠøŠŗŠ°=%ls, Šæуть Š½Š°Š·Š½Š°Ń‡ŠµŠ½Šøя=%ls, %ls + + + + %2u: Hard disk image: source image=%ls, target path=%ls, %ls + (change target path with "--vsys %u --unit %u --disk path"; + disable with "--vsys %u --unit %u --ignore") + + %2u: ŠžŠ±Ń€Š°Š· Š¶ŠµŃŃ‚ŠŗŠ¾Š³Š¾ Š“ŠøсŠŗŠ°: Š¾Š±Ń€Š°Š· ŠøстŠ¾Ń‡Š½ŠøŠŗŠ°=%ls, Šæуть Š½Š°Š·Š½Š°Ń‡ŠµŠ½Šøя=%ls, %ls + (ŠøŠ·Š¼ŠµŠ½Šøть Šæуть Š½Š°Š·Š½Š°Ń‡ŠµŠ½Šøя чŠµŃ€ŠµŠ· "--vsys %u --unit %u --disk Šæуть"; + Š¾Ń‚ŠŗŠ»ŃŽŃ‡Šøть чŠµŃ€ŠµŠ· "--vsys %u --unit %u --ignore") + + + + + Option --ImportToVDI can not be used together with a manually set target path. + + + + + Invalid controller value: '%s' + + + + + + Invalid storage controller specified: %u + + + + + Invalid port value: '%s' + + + + + Failed to extract controller value from ExtraConfig: '%s' + + + + + Failed to extract channel value from ExtraConfig: '%s' + + + + + Device already attached to controller %u at this port (%u) location. + + + + + Illegal port value: %u. For %ls controllers the only valid values are 0 to %lu (inclusive) + + + + + %2u: Hard disk image specified with --disk: source image=%ls, target path=%ls, %s + (change controller with "--vsys %u --unit %u --controller <index>"; + change controller port with "--vsys %u --unit %u --port <n>") + + + + + + %2u: Hard disk image specified with --disk and --controller: source image=%ls, target path=%ls, %s + (change controller port with "--vsys %u --unit %u --port <n>") + + + + + + %2u: Hard disk image specified with --disk and --port: source image=%ls, target path=%ls, %s + (change controller with "--vsys %u --unit %u --controller <index>") + + + + + + %2u: Hard disk image specified with --controller and --port: source image=%ls, target path=%ls, %s + (change target path with "--vsys %u --unit %u --disk path") + + + + + + %2u: Hard disk image specified with --port: source image=%ls, target path=%ls, %s + (change target path with "--vsys %u --unit %u --disk path"; + change controller with "--vsys %u --unit %u --controller <index>") + + + + + + %2u: Hard disk image specified with --controller: source image=%ls, target path=%ls, %s + (change target path with "--vsys %u --unit %u --disk path"; + change controller port with "--vsys %u --unit %u --port <n>") + + + + + + %2u: Hard disk image specified with --disk and --controller and --port: source image=%ls, target path=%ls, %s + + + + + + %2u: Hard disk image: source image=%ls, target path=%ls, %s + (change target path with "--vsys %u --unit %u --disk path"; + change controller with "--vsys %u --unit %u --controller <index>"; + change controller port with "--vsys %u --unit %u --port <n>"; + disable with "--vsys %u --unit %u --ignore") + + + + + + %2u: CD-ROM -- disabled + + %2u: CD-ROM -- Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½ + + + + + %2u: CD-ROM + (disable with "--vsys %u --unit %u --ignore") + + %2u: CD-ROM + (Š¾Ń‚ŠŗŠ»ŃŽŃ‡Šøть чŠµŃ€ŠµŠ· "--vsys %u --unit %u --ignore") + + + + + %2u: Floppy -- disabled + + %2u: Š¤Š»Š¾ŠæŠæŠø -- Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½ + + + + + %2u: Floppy + (disable with "--vsys %u --unit %u --ignore") + + %2u: Š¤Š»Š¾ŠæŠæŠø + (Š¾Ń‚ŠŗŠ»ŃŽŃ‡Šøть чŠµŃ€ŠµŠ· "--vsys %u --unit %u --ignore") + + + + + %2u: Network adapter: orig %ls, config %ls, extra %ls + + %2u: Š”ŠµŃ‚ŠµŠ²Š¾Š¹ Š°Š“Š°ŠæтŠµŃ€: Š¾Ń€ŠøŠ³. %ls, ŠŗŠ¾Š½Ń„ŠøŠ³. %ls, эŠŗстрŠ° %ls + + + + + %2u: USB controller -- disabled + + %2u: USB ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€ -- Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½ + + + + + %2u: USB controller + (disable with "--vsys %u --unit %u --ignore") + + %2u: USB ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€ + (Š¾Ń‚ŠŗŠ»ŃŽŃ‡Šøть чŠµŃ€ŠµŠ· "--vsys %u --unit %u --ignore") + + + + + %2u: Sound card "%ls" -- disabled + + %2u: ŠŃƒŠ“ŠøŠ¾ ŠŗŠ°Ń€Ń‚Š° "%ls" -- Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½Š° + + + + + %2u: Sound card (appliance expects "%ls", can change on import) + (disable with "--vsys %u --unit %u --ignore") + + %2u: ŠŃƒŠ“ŠøŠ¾ ŠŗŠ°Ń€Ń‚Š° (ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†Šøя Š¾Š¶ŠøŠ“Š°ŠµŃ‚ "%ls", Š¼Š¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ ŠøŠ·Š¼ŠµŠ½ŠµŠ½Š° ŠæрŠø ŠøŠ¼ŠæŠ¾Ń€Ń‚Šµ) + (Š¾Ń‚ŠŗŠ»ŃŽŃ‡Šøть чŠµŃ€ŠµŠ· "--vsys %u --unit %u --ignore") + + + + + %2u: VM settings file name specified with --settingsfile: "%ls" + + %2u: Š˜Š¼Ń фŠ°Š¹Š»Š° Š½Š°ŃŃ‚Ń€Š¾ŠµŠŗ Š’Šœ уŠŗŠ°Š·Š°Š½Š½Š¾Šµ чŠµŃ€ŠµŠ· --settingsfile: "%ls" + + + + + %2u: Suggested VM settings file name "%ls" + (change with "--vsys %u --settingsfile <filename>") + + %2u: ŠŸŃ€ŠµŠ“Š»Š¾Š¶ŠµŠ½Š½Š¾Šµ ŠøŠ¼Ń фŠ°Š¹Š»Š° Š½Š°ŃŃ‚Ń€Š¾ŠµŠŗ Š’Šœ "%ls" + (ŠøŠ·Š¼ŠµŠ½Šøть чŠµŃ€ŠµŠ· "--vsys %u --settingsfile <ŠøŠ¼Ń фŠ°Š¹Š»Š°>") + + + + + %2u: VM base folder specified with --basefolder: "%ls" + + %2u: ŠžŃŠ½Š¾Š²Š½Š°Ń ŠæŠ°ŠæŠŗŠ° Š’Šœ уŠŗŠ°Š·Š°Š½Š½Š°Ń чŠµŃ€ŠµŠ· --basefolder: "%ls" + + + + + %2u: Suggested VM base folder "%ls" + (change with "--vsys %u --basefolder <path>") + + %2u: ŠŸŃ€ŠµŠ“Š»Š¾Š¶ŠµŠ½Š½Š°Ń Š¾ŃŠ½Š¾Š²Š½Š°Ń ŠæŠ°ŠæŠŗŠ° Š’Šœ "%ls" + (ŠøŠ·Š¼ŠµŠ½Šøть чŠµŃ€ŠµŠ· "--vsys %u --basefolder <Šæуть>") + + + + + %2u: VM group specified with --group: "%ls" + + %2u: Š“Ń€ŃƒŠæŠæŠ° Š’Šœ, уŠŗŠ°Š·Š°Š½Š½Š°Ń чŠµŃ€ŠµŠ· --group: "%ls" + + + + + %2u: Suggested VM group "%ls" + (change with "--vsys %u --group <group>") + + %2u: ŠŸŃ€ŠµŠ“Š»Š¾Š¶ŠµŠ½Š½Š°Ń Š³Ń€ŃƒŠæŠæŠ° Š’Šœ "%ls" + (ŠøŠ·Š¼ŠµŠ½Šøть чŠµŃ€ŠµŠ· "--vsys %u --group <Š³Ń€ŃƒŠæŠæŠ°>") + + + + + %2u: Suggested cloud shape "%ls" + + %2u: ŠŸŃ€ŠµŠ“Š»Š¾Š¶ŠµŠ½Š½Š°Ń Š¾Š±Š»Š°Ń‡Š½Š°Ń фŠ¾Ń€Š¼Š° "%ls" + + + + + %2u: Cloud bucket id specified with --cloudbucket: "%ls" + + %2u: ID Š¾Š±Š»Š°Ń‡Š½Š¾Š¹ ŠŗŠ¾Ń€Š·ŠøŠ½Ń‹, уŠŗŠ°Š·Š°Š½Š½Ń‹Š¹ чŠµŃ€ŠµŠ· --cloudbucket: "%ls" + + + + + %2u: Suggested cloud bucket id "%ls" + (change with "--cloud %u --cloudbucket <id>") + + %2u: ŠŸŃ€ŠµŠ“Š»Š¾Š¶ŠµŠ½Š½Ń‹Š¹ ID Š¾Š±Š»Š°Ń‡Š½Š¾Š¹ ŠŗŠ¾Ń€Š·ŠøŠ½Ń‹ "%ls" + (ŠøŠ·Š¼ŠµŠ½Šøть чŠµŃ€ŠµŠ· "--cloud %u --cloudbucket <id>") + + + + + %2u: Cloud profile name specified with --cloudprofile: "%ls" + + %2u: Š˜Š¼Ń Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ ŠæрŠ¾Ń„ŠøŠ»Ń, уŠŗŠ°Š·Š°Š½Š½Š¾Š³Š¾ чŠµŃ€ŠµŠ· --cloudprofile: "%ls" + + + + + %2u: Suggested cloud profile name "%ls" + (change with "--cloud %u --cloudprofile <id>") + + %2u: ŠŸŃ€ŠµŠ“Š»Š¾Š¶ŠµŠ½Š½Š¾Šµ ŠøŠ¼Ń Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ ŠæрŠ¾Ń„ŠøŠ»Ń "%ls" + (ŠøŠ·Š¼ŠµŠ½Šøть чŠµŃ€ŠµŠ· "--cloud %u --cloudprofile <id>") + + + + + %2u: Cloud instance id specified with --cloudinstanceid: "%ls" + + %2u: ID Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š°, уŠŗŠ°Š·Š°Š½Š½Ń‹Š¹ чŠµŃ€ŠµŠ· --cloudinstanceid: "%ls" + + + + + %2u: Suggested cloud instance id "%ls" + (change with "--cloud %u --cloudinstanceid <id>") + + %2u: ŠŸŃ€ŠµŠ“Š»Š¾Š¶ŠµŠ½Š½Ń‹Š¹ ID Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š° "%ls" + (ŠøŠ·Š¼ŠµŠ½Šøть чŠµŃ€ŠµŠ· "--cloud %u --cloudinstanceid <id>") + + + + + %2u: Suggested cloud base image id "%ls" + + %2u: ŠŸŃ€ŠµŠ“Š»Š¾Š¶ŠµŠ½Š½Ń‹Š¹ ID Suggested cloud base image id "%ls" + + + + + Cannot import until the license agreement listed above is accepted. + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæрŠ¾ŠøŠ·Š²ŠµŃŃ‚Šø ŠøŠ¼ŠæŠ¾Ń€Ń‚ ŠæŠ¾ŠŗŠ° Š½Šµ ŠæрŠøŠ½ŃŃ‚Ń‹ усŠ»Š¾Š²Šøя Š»ŠøцŠµŠ½Š·ŠøŠ¾Š½Š½Š¾Š³Š¾ сŠ¾Š³Š»Š°ŃˆŠµŠ½Šøя, ŠæŠ¾ŠŗŠ°Š·Š°Š½Š½Ń‹Šµ Š²Ń‹ŃˆŠµ. + + + + Cannot import until the %c license agreements listed above are accepted. + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæрŠ¾ŠøŠ·Š²ŠµŃŃ‚Šø ŠøŠ¼ŠæŠ¾Ń€Ń‚ ŠæŠ¾ŠŗŠ° Š½Šµ ŠæрŠøŠ½ŃŃ‚Ń‹ усŠ»Š¾Š²Šøя Š»ŠøцŠµŠ½Š·ŠøŠ¾Š½Š½Š¾Š³Š¾ сŠ¾Š³Š»Š°ŃˆŠµŠ½Šøя %c, ŠæŠ¾ŠŗŠ°Š·Š°Š½Š½Ń‹Šµ Š²Ń‹ŃˆŠµ. + + + + Appliance import failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾ŠøŠ·Š²ŠµŃŃ‚Šø ŠøŠ¼ŠæŠ¾Ń€Ń‚ ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†ŠøŠø + + + + Successfully imported the appliance. + + ŠšŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†Šøя ŠøŠ¼ŠæŠ¾Ń€Ń‚ŠøрŠ¾Š²Š°Š½Š° усŠæŠµŃˆŠ½Š¾. + + + + + You can only specify --output once. + --output Š¼Š¾Š¶Š½Š¾ уŠŗŠ°Š·Š°Ń‚ŃŒ тŠ¾Š»ŃŒŠŗŠ¾ Š¾Š“ŠøŠ½ рŠ°Š·. + + + + Invalid export options '%s' + + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Šµ Š¾ŠæцŠøŠø ŠøŠ¼ŠæŠ¾Ń€Ń‚Š° '%s' + + + + + unhandled option: -%c + Š½ŠµŠ¾Š±Ń€Š°Š±Š¾Ń‚Š°Š½Š½Š°Ń Š¾ŠæцŠøя: -%c + + + + unhandled option: %i + Š½ŠµŠ¾Š±Ń€Š°Š±Š¾Ń‚Š°Š½Š½Š°Ń Š¾ŠæцŠøя: %i + + + + unknown option: %s + Š½ŠµŠøŠ·Š²ŠµŃŃ‚Š½Š°Ń Š¾ŠæцŠøя: %s + + + + At least one machine must be specified with the export command. + ŠšŠ°Šŗ Š¼ŠøŠ½ŠøŠ¼ŃƒŠ¼ Š¾Š“Š½Š° Š¼Š°ŃˆŠøŠ½Š° Š“Š¾Š»Š¶Š½Š° Š±Ń‹Ń‚ŃŒ уŠŗŠ°Š·Š°Š½Š° Š² ŠŗŠ¾Š¼Š°Š½Š“Šµ эŠŗсŠæŠ¾Ń€Ń‚Š°. + + + + Missing --output argument with export command. + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ --output у ŠŗŠ¾Š¼Š°Š½Š“ы эŠŗсŠæŠ¾Ń€Ń‚Š°. + + + + Invalid index %RI32 with -vsys option; you specified only %zu virtual system(s). + + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠøŠ½Š“ŠµŠŗс %RI32 у Š¾ŠæцŠøŠø -vsys option; Š²Ń‹ уŠŗŠ°Š·Š°Š»Šø тŠ¾Š»ŃŒŠŗŠ¾ %zu Š²ŠøртуŠ°Š»ŃŒŠ½ŃƒŃŽ сŠøстŠµŠ¼Ńƒ. + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠøŠ½Š“ŠµŠŗс %RI32 у Š¾ŠæцŠøŠø -vsys option; Š²Ń‹ уŠŗŠ°Š·Š°Š»Šø тŠ¾Š»ŃŒŠŗŠ¾ %zu Š²ŠøртуŠ°Š»ŃŒŠ½Ń‹Šµ сŠøстŠµŠ¼Ń‹. + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠøŠ½Š“ŠµŠŗс %RI32 у Š¾ŠæцŠøŠø -vsys option; Š²Ń‹ уŠŗŠ°Š·Š°Š»Šø тŠ¾Š»ŃŒŠŗŠ¾ %zu Š²ŠøртуŠ°Š»ŃŒŠ½Ń‹Ń… сŠøстŠµŠ¼. + + + + + Cannot read license file "%s" which should be included in the virtual system %u. + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæрŠ¾Ń‡ŠµŃŃ‚ŃŒ фŠ°Š¹Š» Š»ŠøцŠµŠ½Š·ŠøŠø "%s", ŠŗŠ¾Ń‚Š¾Ń€Ń‹Š¹ Š“Š¾Š»Š¶ŠµŠ½ Š±Ń‹Ń‚ŃŒ Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½ Š² Š²ŠøртуŠ°Š»ŃŒŠ½ŃƒŃŽ сŠøстŠµŠ¼Ńƒ %u. + + + + Enter the passwords for the following identifiers to export the apppliance: + + Š’Š²ŠµŠ“ŠøтŠµ ŠæŠ°Ń€Š¾Š»ŃŒ Š“Š»Ń сŠ»ŠµŠ“ующŠøх ŠøŠ“ŠµŠ½Ń‚ŠøфŠøŠŗŠ°Ń‚Š¾Ń€Š¾Š², чтŠ¾Š±Ń‹ эŠŗсŠæŠ¾Ń€Ń‚ŠøрŠ¾Š²Š°Ń‚ŃŒ ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†Šøю: + + + + + Password ID %s: + ID ŠæŠ°Ń€Š¾Š»Ń %s: + + + + Appliance write failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°ŠæŠøсŠ°Ń‚ŃŒ ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†Šøю + + + + Successfully exported %d machine(s). + + + %d Š¼Š°ŃˆŠøŠ½Š° эŠŗсŠæŠ¾Ń€Ń‚ŠøрŠ¾Š²Š°Š½Š° усŠæŠµŃˆŠ½Š¾. + + %d Š¼Š°ŃˆŠøŠ½Ń‹ эŠŗсŠæŠ¾Ń€Ń‚ŠøрŠ¾Š²Š°Š½Ń‹ усŠæŠµŃˆŠ½Š¾. + + %d Š¼Š°ŃˆŠøŠ½ эŠŗсŠæŠ¾Ń€Ń‚ŠøрŠ¾Š²Š°Š½Š¾ усŠæŠµŃˆŠ½Š¾. + + + + + + Creating a cloud instance... + + Š”Š¾Š·Š“Š°Š½ŠøŠµ Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š°... + + + + + Creating the cloud instance failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Š·Š“Š°Ń‚ŃŒ Š¾Š±Š»Š°Ń‡Š½Ń‹Š¹ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€ + + + + A cloud instance with id '%s' (provider '%s') was created + + ŠžŠ±Š»Š°Ń‡Š½Ń‹Š¹ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€ с ID '%s' (ŠæрŠ¾Š²Š°Š¹Š“ŠµŃ€ '%s') сŠ¾Š·Š“Š°Š½ + + + + + Failed to open OVA '%s' for updating: %Rrc + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š¾Ń‚Šŗрыть OVA '%s' Š“Š»Ń Š¾Š±Š½Š¾Š²Š»ŠµŠ½Šøя: %Rrc + + + + Failed to open OVA '%s' as a TAR file: %Rrc + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š¾Ń‚Šŗрыть OVA '%s' ŠŗŠ°Šŗ фŠ°Š¹Š» TAR: %Rrc + + + + Scanning OVA '%s' for a manifest and signature... + Š”ŠŗŠ°Š½ŠøрŠ¾Š²Š°Š½ŠøŠµ OVA '%s' Š½Š° Š½Š°Š»ŠøчŠøŠµ Š¼Š°Š½ŠøфŠµŃŃ‚Š° Šø ŠæŠ¾Š“ŠæŠøсŠø... + + + + RTVfsFsStrmNext returned %Rrc + RTVfsFsStrmNext Š²Š¾Š·Š²Ń€Š°Ń‚ŠøŠ» %Rrc + + + + OVA contains multiple manifests! first: %s second: %s + OVA сŠ¾Š“ŠµŃ€Š¶Šøт Š½ŠµŃŠŗŠ¾Š»ŃŒŠŗŠ¾ Š¼Š°Š½ŠøфŠµŃŃ‚Š¾Š²! ŠæŠµŃ€Š²Ń‹Š¹: %s Š²Ń‚Š¾Ń€Š¾Š¹: %s + + + + + Unsupported OVA file ordering! Signature file ('%s') as succeeded by '%s'. + ŠŠµŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŠ¼Ń‹Š¹ ŠæŠ¾Ń€ŃŠ“Š¾Šŗ фŠ°Š¹Š»Š¾Š² OVA! Š¤Š°Š¹Š» ŠæŠ¾Š“ŠæŠøсŠø ('%s') ŠæŠ¾ŃŠ»Šµ '%s'. + + + + Found manifest file: %s + ŠŠ°Š¹Š“ŠµŠ½ фŠ°Š¹Š» Š¼Š°Š½ŠøфŠµŃŃ‚Š°: %s + + + + Failed to memorize the manifest: %Rrc + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°ŠæŠ¾Š¼Š½Šøть Š¼Š°Š½ŠøфŠµŃŃ‚: %Rrc + + + + + + + Out of memory! + ŠŠµ хŠ²Š°Ń‚Š°ŠµŃ‚ ŠæŠ°Š¼ŃŃ‚Šø! + + + + Multiple signature files! (%s) + ŠŠµŃŠŗŠ¾Š»ŃŒŠŗŠ¾ фŠ°Š¹Š»Š¾Š² ŠæŠ¾Š“ŠæŠøсŠµŠ¹! (%s) + + + + Found existing signature file: %s + ŠŠ°Š¹Š“ŠµŠ½ сущŠµŃŃ‚Š²ŃƒŃŽŃ‰ŠøŠ¹ фŠ°Š¹Š» сŠøŠ³Š½Š°Ń‚ŃƒŃ€Ń‹: %s + + + + The OVA contains no manifest and cannot be signed! + OVA Š½Šµ сŠ¾Š“ŠµŃ€Š¶Šøт Š¼Š°Š½ŠøфŠµŃŃ‚Š° Šø Š½Šµ Š¼Š¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ ŠæŠ¾Š“ŠæŠøсŠ°Š½! + + + + The OVA is already signed ('%s')! (Use the --force option to force re-signing it.) + OVA уŠ¶Šµ ŠæŠ¾Š“ŠæŠøсŠ°Š½ ('%s')! (Š˜ŃŠæŠ¾Š»ŃŒŠ·ŃƒŠ¹Ń‚Šµ Š¾ŠæцŠøю --force, чтŠ¾Š±Ń‹ Š·Š°Š½Š¾Š²Š¾ ŠæŠ¾Š“ŠæŠøсŠ°Ń‚ŃŒ ŠµŠ³Š¾.) + + + + Writing '%s' to the OVA... + Š—Š°ŠæŠøсь '%s' Š² OVA... + + + + RTZipTarFsStreamTruncate failed on '%s': %Rrc + RTZipTarFsStreamTruncate Š·Š°Š²ŠµŃ€ŃˆŠøŠ»ŃŃ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹ Š½Š° '%s': %Rrc + + + + RTVfsFileSeek(hVfsFileSignature) failed: %Rrc + RTVfsFileSeek(hVfsFileSignature) Š·Š°Š²ŠµŃ€ŃˆŠøŠ»ŃŃ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹: %Rrc + + + + RTVfsFsStrmAdd('%s') failed on '%s': %Rrc + RTVfsFsStrmAdd('%s') Š·Š°Š²ŠµŃ€ŃˆŠøŠ»ŃŃ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹ Š½Š° '%s': %Rrc + + + + RTVfsFsStrmEnd failed on '%s': %Rrc + RTVfsFsStrmEnd Š·Š°Š²ŠµŃ€ŃˆŠøŠ»ŃŃ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹ Š½Š° '%s': %Rrc + + + + Successfully decoded the PKCS#7/CMS signature... + PKCS#7/CMS ŠæŠ¾Š“ŠæŠøсь Š“ŠµŠŗŠ¾Š“ŠøрŠ¾Š²Š°Š½Š° усŠæŠµŃˆŠ½Š¾... + + + + Successfully verified the PKCS#7/CMS signature + PKCS#7/CMS ŠæŠ¾Š“ŠæŠøсь ŠæрŠ¾Š²ŠµŃ€ŠµŠ½Š° усŠæŠµŃˆŠ½Š¾ + + + + Failed to verify the PKCS#7/CMS signature: %Rrc%RTeim + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾Š²ŠµŃ€Šøть PKCS#7/CMS ŠæŠ¾Š“ŠæŠøсь: %Rrc%RTeim + + + + RTCrPkcs7SignedData_CheckSanity failed on PKCS#7/CMS signature: %Rrc%RTeim + RTCrPkcs7SignedData_CheckSanity Š·Š°Š²ŠµŃ€ŃˆŠµŠ½Š° с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹ Š½Š° PKCS#7/CMS ŠæŠ¾Š“ŠæŠøсŠø: %Rrc%RTeim + + + + PKCS#7/CMS signature inner ContentType isn't 'data' but: %s + Š’Š½ŃƒŃ‚Ń€ŠµŠ½Š½ŠµŠµ ŠæŠ¾Š»Šµ ContentType PKCS#7/CMS ŠæŠ¾Š“ŠæŠøсŠø яŠ²Š»ŃŠµŃ‚ся Š½Šµ Š“Š°Š½Š½Ń‹Š¼Šø, Š°: %s + + + + PKCS#7/CMD signature is not 'signedData': %s + PKCS#7/CMD ŠæŠ¾Š“ŠæŠøсь Š½Šµ 'signedData': %s + + + + RTCrPkcs7ContentInfo_Clone failed: %Rrc + RTCrPkcs7ContentInfo_Clone Š·Š°Š²ŠµŃ€ŃˆŠµŠ½ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹: %Rrc + + + + RTCrPkcs7ContentInfo_DecodeAsn1 failed to decode PKCS#7/CMS signature: %Rrc%RTemi + RTCrPkcs7ContentInfo_DecodeAsn1 Š½Šµ сŠ¼Š¾Š³ Š“ŠµŠŗŠ¾Š“ŠøрŠ¾Š²Š°Ń‚ŃŒ PKCS#7/CMS ŠæŠ¾Š“ŠæŠøсь: %Rrc%RTemi + + + + Manifest is too big: %#RX64 bytes, max 4MiB + + ŠœŠ°Š½ŠøфŠµŃŃ‚ сŠ»ŠøшŠŗŠ¾Š¼ Š±Š¾Š»ŃŒŃˆŠ¾Š¹: %#RX64 Š±Š°Š¹Ń‚, Š¼Š°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ 4MiB + ŠœŠ°Š½ŠøфŠµŃŃ‚ сŠ»ŠøшŠŗŠ¾Š¼ Š±Š¾Š»ŃŒŃˆŠ¾Š¹: %#RX64 Š±Š°Š¹Ń‚Š°, Š¼Š°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ 4MiB + ŠœŠ°Š½ŠøфŠµŃŃ‚ сŠ»ŠøшŠŗŠ¾Š¼ Š±Š¾Š»ŃŒŃˆŠ¾Š¹: %#RX64 Š±Š°Š¹Ń‚, Š¼Š°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ 4MiB + + + + + RTCrStoreCertAddFromFile failed on '%s': %Rrc%#RTeim + RTCrStoreCertAddFromFile Š·Š°Š²ŠµŃ€ŃˆŠøŠ»ŃŃ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹ Š½Š° '%s': %Rrc%#RTeim + + + + RTCrStoreCreateInMem failed: %Rrc + RTCrStoreCreateInMem Š·Š°Š²ŠµŃ€ŃˆŠøŠ»ŃŃ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹: %Rrc + + + + Created PKCS#7/CMS signature: %zu bytes, %s. + + PKCS#7/CMS ŠæŠ¾Š“ŠæŠøсь сŠ¾Š·Š“Š°Š½Š°: %zu Š±Š°Š¹Ń‚, %s. + PKCS#7/CMS ŠæŠ¾Š“ŠæŠøсь сŠ¾Š·Š“Š°Š½Š°: %zu Š±Š°Š¹Ń‚Š°, %s. + PKCS#7/CMS ŠæŠ¾Š“ŠæŠøсь сŠ¾Š·Š“Š°Š½Š°: %zu Š±Š°Š¹Ń‚, %s. + + + + + Using SHA-1 instead of SHA-3 for the PKCS#7/CMS signature. + Š˜ŃŠæŠ¾Š»ŃŒŠ·ŃƒŠµŃ‚ся SHA-1 Š²Š¼ŠµŃŃ‚Š¾ SHA-3 Š“Š»Ń PKCS#7/CMS ŠæŠ¾Š“ŠæŠøсŠø. + + + + RTCrPemWriteBlobToVfsFile failed: %Rrc + RTCrPemWriteBlobToVfsFile Š·Š°Š²ŠµŃ€ŃˆŠøŠ»ŃŃ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹: %Rrc + + + + RTCrPkcs7SimpleSignSignedData failed: %Rrc%#RTeim + RTCrPkcs7SimpleSignSignedData Š·Š°Š²ŠµŃ€ŃˆŠøŠ»ŃŃ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹: %Rrc%#RTeim + + + + RTVfsFileReadAt failed: %Rrc + RTVfsFileReadAt Š·Š°Š²ŠµŃ€ŃˆŠøŠ»ŃŃ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹: %Rrc + + + + Unsupported digest type: %s + ŠŠµŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŠ¼Ń‹Š¹ тŠøŠæ Š“Š°Š¹Š“Š¶ŠµŃŃ‚Š°: %s + + + + Failed to create digest for %s: %Rrc + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Š·Š“Š°Ń‚ŃŒ Š“Š°Š¹Š“Š¶ŠµŃŃ‚ Š“Š»Ń %s: %Rrc + + + + Created OVA signature: %zu bytes, %s + + ŠŸŠ¾Š“ŠæŠøсь OVA сŠ¾Š·Š“Š°Š½Š°: %zu Š±Š°Š¹Ń‚, %s + ŠŸŠ¾Š“ŠæŠøсь OVA сŠ¾Š·Š“Š°Š½Š°: %zu Š±Š°Š¹Ń‚Š°, %s + ŠŸŠ¾Š“ŠæŠøсь OVA сŠ¾Š·Š“Š°Š½Š°: %zu Š±Š°Š¹Ń‚, %s + + + + + Successfully decoded and verified the OVA signature. + + Š”ŠøŠ³Š½Š°Ń‚ŃƒŃ€Š° OVA Š“ŠµŠŗŠ¾Š“ŠøрŠ¾Š²Š°Š½Š° Šø ŠæрŠ¾Š²ŠµŃ€ŠµŠ½Š° усŠæŠµŃˆŠ½Š¾. + + + + + Failed to write certificate to signature file: %Rrc%#RTeim + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°ŠæŠøсŠ°Ń‚ŃŒ сŠµŃ€Ń‚ŠøфŠøŠŗŠ°Ń‚ Š² фŠ°Š¹Š» ŠæŠ¾Š“ŠæŠøсŠø: %Rrc%#RTeim + + + + Failed to produce signature file: %Rrc + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ“ŠµŠ»Š°Ń‚ŃŒ фŠ°Š¹Š» сŠøŠ³Š½Š°Ń‚ŃƒŃ€Ń‹: %Rrc + + + + RTVfsMemFileCreate failed: %Rrc + RTVfsMemFileCreate Š·Š°Š²ŠµŃ€ŃˆŠµŠ½ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹: %Rrc + + + + Encountered a problem when validating the signature we just created: %Rrc%#RTeim +Please make sure the certificate and private key matches. + + + + Encountered a problem when validating the signature we just created: %Rrc%#RTeim +Plase make sure the certificate and private key matches. + Š”тŠ¾Š»ŠŗŠ½ŃƒŠ»Šøсь с ŠæрŠ¾Š±Š»ŠµŠ¼Š¾Š¹ ŠæрŠø ŠæрŠ¾Š²ŠµŃ€ŠŗŠµ тŠ¾Š»ŃŒŠŗŠ¾ чтŠ¾ сŠ¾Š·Š“Š°Š½Š½Š¾Š¹ ŠæŠ¾Š“ŠæŠøсŠø: %Rrc%#RTeim +ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, уŠ±ŠµŠ“ŠøтŠµŃŃŒ, чтŠ¾ сŠµŃ€Ń‚ŠøфŠøŠŗŠ°Ń‚ Šø ŠæрŠøŠ²Š°Ń‚Š½Ń‹Š¹ ŠŗŠ»ŃŽŃ‡ сŠ¾Š¾Ń‚Š²ŠµŃ‚стŠ²ŃƒŃŽŃ‚ Š“руŠ³ Š“руŠ³Ńƒ. + + + + 2nd RTCrPkixPubKeySignDigest call failed: %Rrc%#RTeim + Š’Ń‚Š¾Ń€Š¾Š¹ Š²Ń‹Š·Š¾Š² RTCrPkixPubKeySignDigest Š·Š°Š²ŠµŃ€ŃˆŠøŠ»ŃŃ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹: %Rrc%#RTeim + + + + RTCrPkixPubKeySignDigest failed: %Rrc%#RTeim + RTCrPkixPubKeySignDigest Š·Š°Š²ŠµŃ€ŃˆŠøŠ»ŃŃ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹: %Rrc%#RTeim + + + + Failed to create digest %s: %Rrc + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Š·Š“Š°Ń‚ŃŒ Š“Š°Š¹Š“Š¶ŠµŃŃ‚ %s: %Rrc + + + + + Password is given more than once. + ŠŸŠ°Ń€Š¾Š»ŃŒ уŠŗŠ°Š·Š°Š½ Š½ŠµŃŠŗŠ¾Š»ŃŒŠŗŠ¾ рŠ°Š·. + + + + Unknown digest type: %s + ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Ń‹Š¹ тŠøŠæ Š“Š°Š¹Š“Š¶ŠµŃŃ‚Š°: %s + + + + Too many intermediate certificates: max %zu + Š”Š»ŠøшŠŗŠ¾Š¼ Š¼Š½Š¾Š³Š¾ ŠæрŠ¾Š¼ŠµŠ¶ŃƒŃ‚Š¾Ń‡Š½Ń‹Ń… сŠµŃ€Ń‚ŠøфŠøŠŗŠ°Ń‚Š¾Š²: Š¼Š°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ %zu + + + + No OVA file was specified! + ŠŠµ уŠŗŠ°Š·Š°Š½ фŠ°Š¹Š» OVA! + + + + No signing certificate (--certificate=<file>) was specified! + ŠŠµ уŠŗŠ°Š·Š°Š½ сŠµŃ€Ń‚ŠøфŠøŠŗŠ°Ń‚ (--certificate=<фŠ°Š¹Š»>)! + + + + No signing private key (--private-key=<file>) was specified! + ŠŠµ уŠŗŠ°Š·Š°Š½ ŠæрŠøŠ²Š°Ń‚Š½Ń‹Š¹ ŠŗŠ»ŃŽŃ‡ (--private-key=<фŠ°Š¹Š»>)! + + + + The specified OVA file was not found: %s + ŠŠµ Š½Š°Š¹Š“ŠµŠ½ уŠŗŠ°Š·Š°Š½Š½Ń‹Š¹ фŠ°Š¹Š» OVA: %s + + + + The specified certificate file was not found: %s + Š£ŠŗŠ°Š·Š°Š½Š½Ń‹Š¹ фŠ°Š¹Š» сŠµŃ€Ń‚ŠøфŠøŠŗŠ°Ń‚Š° Š½Šµ Š½Š°Š¹Š“ŠµŠ½: %s + + + + The specified private key file was not found: %s + Š£ŠŗŠ°Š·Š°Š½Š½Ń‹Š¹ фŠ°Š¹Š» ŠæрŠøŠ²Š°Ń‚Š½Š¾Š³Š¾ ŠŗŠ»ŃŽŃ‡Š° Š½Šµ Š½Š°Š¹Š“ŠµŠ½: %s + + + + Error reading certificate from '%s': %Rrc%#RTeim + ŠžŃˆŠøŠ±ŠŗŠ° чтŠµŠ½Šøя сŠµŃ€Ń‚ŠøфŠøŠŗŠ°Ń‚Š° ŠøŠ· '%s': %Rrc%#RTeim + + + + Successfully read the certificate and private key. + Š£ŃŠæŠµŃˆŠ½Š¾ ŠæрŠ¾Ń‡Ń‚ŠµŠ½Ń‹ сŠµŃ€Ń‚ŠøфŠøŠŗŠ°Ń‚ Šø ŠæрŠøŠ²Š°Ń‚Š½Ń‹Š¹ ŠŗŠ»ŃŽŃ‡. + + + + Successfully signed '%s'. + '%s' ŠæŠ¾Š“ŠæŠøсŠ°Š½ усŠæŠµŃˆŠ½Š¾. + + + + Error reading the private key from %s: %Rrc%#RTeim + ŠžŃˆŠøŠ±ŠŗŠ° чтŠµŠ½Šøя ŠæрŠøŠ²Š°Ń‚Š½Š¾Š³Š¾ ŠŗŠ»ŃŽŃ‡Š° ŠøŠ· %s: %Rrc%#RTeim + + + + BWControl + + + + Limit is too big + + Š›ŠøŠ¼Šøт сŠ»ŠøшŠŗŠ¾Š¼ Š±Š¾Š»ŃŒŃˆŠ¾Š¹ + + + + + Invalid unit suffix. Valid suffixes are: k, m, g, K, M, G + + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ суффŠøŠŗс ŠµŠ“ŠøŠ½Šøцы ŠøŠ·Š¼ŠµŃ€ŠµŠ½Šøя. Š”Š¾ŠæустŠøŠ¼Ń‹Šµ суффŠøŠŗсы: k, m, g, K, M, G + + + + + Trailing spaces in limit! + + Š£ Š»ŠøŠ¼ŠøтŠ° ŠæрŠ¾Š±ŠµŠ»Ń‹ Š² ŠŗŠ¾Š½Ń†Šµ стрŠ¾ŠŗŠø! + + + + + No digits in limit specifier + + ŠŠµŃ‚ цŠøфр Š² сŠæŠµŃ†ŠøфŠøŠŗŠ°Ń‚Š¾Ń€Šµ Š»ŠøŠ¼ŠøтŠ° + + + + + Invalid limit specifier + + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ сŠæŠµŃ†ŠøфŠøŠŗŠ°Ń‚Š¾Ń€ Š»ŠøŠ¼ŠøтŠ° + + + + + Limit cannot be negative + + Š›ŠøŠ¼Šøт Š½Šµ Š¼Š¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ Š¾Ń‚Ń€ŠøцŠ°Ń‚ŠµŠ»ŃŒŠ½Ń‹Š¼ + + + + + Bandwidth group name must not be empty! + + Š˜Š¼Ń Š³Ń€ŃƒŠæŠæы ŠæŠ¾Š»Š¾ŃŃ‹ ŠæрŠ¾ŠæусŠŗŠ°Š½Šøя Š½Šµ Š¼Š¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ ŠæустыŠ¼! + + + + + Invalid bandwidth group type + + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ тŠøŠæ Š³Ń€ŃƒŠæŠæы ŠæŠ¾Š»Š¾ŃŃ‹ ŠæрŠ¾ŠæусŠŗŠ°Š½Šøя + + + + + Too few parameters + Š”Š»ŠøшŠŗŠ¾Š¼ Š¼Š°Š»Š¾ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š¾Š² + + + + Too many parameters + Š”Š»ŠøшŠŗŠ¾Š¼ Š¼Š½Š¾Š³Š¾ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š¾Š² + + + + Bandwidth groups cannot be created while the VM is running + + ŠŠµŠ»ŃŒŠ·Ń сŠ¾Š·Š“Š°Š²Š°Ń‚ŃŒ Š³Ń€ŃƒŠæŠæы ŠæŠ¾Š»Š¾ŃŃ‹ ŠæрŠ¾ŠæусŠŗŠ°Š½Šøя ŠæŠ¾ŠŗŠ° рŠ°Š±Š¾Ń‚Š°ŠµŃ‚ Š’Šœ + + + + + Bandwidth groups cannot be deleted while the VM is running + + ŠŠµŠ»ŃŒŠ·Ń уŠ“Š°Š»ŃŃ‚ŃŒ Š³Ń€ŃƒŠæŠæы ŠæŠ¾Š»Š¾ŃŃ‹ ŠæрŠ¾ŠæусŠŗŠ°Š½Šøя ŠæŠ¾ŠŗŠ° рŠ°Š±Š¾Ń‚Š°ŠµŃ‚ Š’Šœ + + + + + Invalid parameter '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ '%s' + + + + Cloud + + + Parameter --provider is required + Š¢Ń€ŠµŠ±ŃƒŠµŃ‚ся ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ --provider + + + + Parameter --profile is required + Š¢Ń€ŠµŠ±ŃƒŠµŃ‚ся ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ --profile + + + + Unknown cloud instance state "%s" + ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Š¾Šµ сŠ¾ŃŃ‚Š¾ŃŠ½ŠøŠµ Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š° "%s" + + + + + Parameter 'compartment' is empty or absent. +Trying to get the compartment from the passed cloud profile '%s' + + ŠŸŠ°Ń€Š°Š¼ŠµŃ‚Ń€ 'compartment' ŠæустŠ¾Š¹ ŠøŠ»Šø Š¾Ń‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚. +ŠŸŠ¾ŠæытŠŗŠ° ŠæŠ¾Š»ŃƒŃ‡Šøть сŠµŠŗцŠøю ŠøŠ· ŠæŠµŃ€ŠµŠ“Š°Š½Š½Š¾Š³Š¾ Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ ŠæрŠ¾Ń„ŠøŠ»Ń '%s' + + + + + + Found the compartment '%s': + + ŠŠ°Š¹Š“ŠµŠ½Š° сŠµŠŗцŠøя '%s': + + + + + + Parameter --compartment-id is required + Š¢Ń€ŠµŠ±ŃƒŠµŃ‚ся ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ --compartment-id + + + + Reply is in the form 'instance name' = 'instance id' + + ŠžŃ‚Š²ŠµŃ‚ Š² фŠ¾Ń€Š¼Š°Ń‚Šµ 'ŠøŠ¼Ń эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š°' = 'ID эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š°' + + + + + Failed to list instances + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠµŃ€ŠµŃ‡ŠøсŠ»Šøть эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Ń‹ + + + + The list of the instances for the cloud profile '%ls' +and compartment '%s': + + Š”ŠæŠøсŠ¾Šŗ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š¾Š² Š“Š»Ń Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ ŠæрŠ¾Ń„ŠøŠ»Ń '%ls' +Šø сŠµŠŗцŠøŠø '%s': + + + + + Unknown cloud image state "%s" + ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Š¾Šµ сŠ¾ŃŃ‚Š¾ŃŠ½ŠøŠµ Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ Š¾Š±Ń€Š°Š·Š° "%s" + + + + Reply is in the form 'image name' = 'image id' + + ŠžŃ‚Š²ŠµŃ‚ Š² фŠ¾Ń€Š¼Š°Ń‚Šµ 'ŠøŠ¼Ń Š¾Š±Ń€Š°Š·Š°' = 'ID Š¾Š±Ń€Š°Š·Š°' + + + + + Failed to list images + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠµŃ€ŠµŃ‡ŠøсŠ»Šøть Š¾Š±Ń€Š°Š·Ń‹ + + + + The list of the images for the cloud profile '%ls' +and compartment '%s': + + Š”ŠæŠøсŠ¾Šŗ Š¾Š±Ń€Š°Š·Š¾Š² Š“Š»Ń Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ ŠæрŠ¾Ń„ŠøŠ»Ń '%ls' +Šø сŠµŠŗцŠøŠø '%s': + + + + + + + + + + + + + + + + + Empty command parameter list, show help. + + Š”ŠæŠøсŠ¾Šŗ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š¾Š² ŠŗŠ¾Š¼Š°Š½Š“ы Šæуст, Š¾Ń‚Š¾Š±Ń€Š°Š¶ŠµŠ½ŠøŠµ сŠæрŠ°Š²ŠŗŠø. + + + + + Warning!!! Public SSH key doesn't present in the passed arguments... + + ŠŸŃ€ŠµŠ“уŠæрŠµŠ¶Š“ŠµŠ½ŠøŠµ!!! ŠŸŃƒŠ±Š»ŠøчŠ½Ń‹Š¹ SSH ŠŗŠ»ŃŽŃ‡ Š½Šµ ŠæрŠµŠ“стŠ°Š²Š»ŠµŠ½ Š² ŠæŠµŃ€ŠµŠ“Š°Š½Š½Ń‹Ń… Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚Š°Ń…... + + + + + Parameters --image-id and --boot-volume-id are mutually exclusive. Only one of them must be presented. + ŠŸŠ°Ń€Š°Š¼ŠµŃ‚ры --image-id Šø --boot-volume-id Š²Š·Š°ŠøŠ¼Š¾ŠøсŠŗŠ»ŃŽŃ‡Š°ŃŽŃ‰ŠøŠµ. ŠœŠ¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ уŠŗŠ°Š·Š°Š½ тŠ¾Š»ŃŒŠŗŠ¾ Š¾Š“ŠøŠ½ ŠøŠ· Š½Šøх. + + + + Missing parameter --image-id or --boot-volume-id. One of them must be presented. + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŃŽŃ‚ ŠæŠ°Ń€Š°Š¼ŠµŃ‚ры --image-id Šø --boot-volume-id. ŠžŠ“ŠøŠ½ ŠøŠ· Š½Šøх Š“Š¾Š»Š¶ŠµŠ½ Š±Ń‹Ń‚ŃŒ уŠŗŠ°Š·Š°Š½. + + + + Checking the cloud image with id '%s'... + + ŠŸŃ€Š¾Š²ŠµŃ€ŠŗŠ° Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ Š¾Š±Ń€Š°Š·Š° с ID '%s'... + + + + + Checking the cloud image failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾Š²ŠµŃ€Šøть Š¾Š±Š»Š°Ń‡Š½Ń‹Š¹ Š¾Š±Ń€Š°Š· + + + + Creating cloud instance with name '%s' from the image '%s'... + + Š”Š¾Š·Š“Š°Š½ŠøŠµ Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š° с ŠøŠ¼ŠµŠ½ŠµŠ¼ '%s' ŠøŠ· Š¾Š±Ń€Š°Š·Š° '%s'... + + + + + Creating cloud instance with name '%s' from the boot volume '%s'... + + Š”Š¾Š·Š“Š°Š½ŠøŠµ Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š° с ŠøŠ¼ŠµŠ½ŠµŠ¼ '%s' ŠøŠ· Š·Š°Š³Ń€Š°Š·ŃƒŠ·Š¾Ń‡Š½Š¾Š³Š¾ тŠ¾Š¼Š° '%s'... + + + + + Creating cloud instance failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Š·Š“Š°Ń‚ŃŒ Š¾Š±Š»Š°Ń‡Š½Ń‹Š¹ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€ + + + + Cloud instance was created successfully + + ŠžŠ±Š»Š°Ń‡Š½Ń‹Š¹ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€ сŠ¾Š·Š“Š°Š½ усŠæŠµŃˆŠ½Š¾ + + + + + + + + + + Duplicate parameter: --id + Š”ŃƒŠ±Š»ŠøрŠ¾Š²Š°Š½Š½Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€: --id + + + + + + + + + Empty parameter: --id + ŠŸŃƒŃŃ‚Š¾Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€: --id + + + + + + + + + Missing parameter: --id + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€: --id + + + + Getting information about cloud instance with id %s... + + ŠŸŠ¾Š»ŃƒŃ‡ŠµŠ½ŠøŠµ ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŠø Š¾Š± Š¾Š±Š»Š°Ń‡Š½Š¾Š¼ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Šµ с ID %s... + + + + + Reply is in the form 'setting name' = 'value' + + ŠžŃ‚Š²ŠµŃ‚ Š² фŠ¾Ń€Š¼Š°Ń‚Šµ 'Š½Š°Š·Š²Š°Š½ŠøŠµ Š½Š°ŃŃ‚Ń€Š¾Š¹ŠŗŠø' = 'Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ' + + + + + Getting information about cloud instance failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š»ŃƒŃ‡Šøть ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†Šøю Š¾Š± Š¾Š±Š»Š°Ń‡Š½Š¾Š¼ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Šµ + + + + Cloud instance info (provider '%s'): + + Š˜Š½Ń„Š¾Ń€Š¼Š°Ń†Šøя Š¾Š± Š¾Š±Š»Š°Ń‡Š½Š¾Š¼ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Šµ (ŠæрŠ¾Š²Š°Š¹Š“ŠµŃ€ '%s'): + + + + + Availability domain = %ls + + Š”Š¾Š¼ŠµŠ½ Š“Š¾ŃŃ‚ŃƒŠæŠ½Š¾ŃŃ‚Šø = %ls + + + + + Availability domain wasn't found + + Š”Š¾Š¼ŠµŠ½ Š“Š¾ŃŃ‚ŃƒŠæŠ½Š¾ŃŃ‚Šø Š½Šµ Š½Š°Š¹Š“ŠµŠ½ + + + + + Instance displayed name = %ls + + ŠžŃ‚Š¾Š±Ń€Š°Š¶Š°ŠµŠ¼Š¾Šµ ŠøŠ¼Ń эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š° = %ls + + + + + Instance displayed name wasn't found + + ŠžŃ‚Š¾Š±Ń€Š°Š¶Š°ŠµŠ¼Š¾Šµ ŠøŠ¼Ń эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š° Š½Šµ Š½Š°Š¹Š“ŠµŠ½Š¾ + + + + + Instance state = %ls + + Š”Š¾ŃŃ‚Š¾ŃŠ½ŠøŠµ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š° = %ls + + + + + Instance state wasn't found + + Š”Š¾ŃŃ‚Š¾ŃŠ½ŠøŠµ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š° Š½Šµ Š½Š°Š¹Š“ŠµŠ½Š¾ + + + + + Instance Id = %ls + + ID эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š° = %ls + + + + + Instance Id wasn't found + + ID эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š° Š½Šµ Š½Š°Š¹Š“ŠµŠ½ + + + + + Instance name = %ls + + Š˜Š¼Ń эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š° = %ls + + + + + Instance name wasn't found + + Š˜Š¼Ń эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š° Š½Šµ Š½Š°Š¹Š“ŠµŠ½Š¾ + + + + + Bootable image Id = %ls + + ID Š·Š°Š³Ń€ŃƒŠ·Š¾Ń‡Š½Š¾Š³Š¾ Š¾Š±Ń€Š°Š·Š° = %ls + + + + + Image Id whom the instance is booted up wasn't found + + ID Š¾Š±Ń€Š°Š·Š°, Š¾Ń‚ŠŗуŠ“Š° Š·Š°Š³Ń€ŃƒŠ¶Š°ŠµŃ‚ся эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€,Š½Šµ Š½Š°Š¹Š“ŠµŠ½ + + + + + Shape of the instance = %ls + + Š¤Š¾Ń€Š¼Š° эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š° = %ls + + + + + The shape of the instance wasn't found + + Š¤Š¾Ń€Š¼Š° эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š° Š½Šµ Š½Š°Š¹Š“ŠµŠ½Š° + + + + + Type of guest OS = %ls + + Š¢ŠøŠæ Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ” = %ls + + + + + Type of guest OS wasn't found + + Š¢ŠøŠæ Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ” Š½Šµ Š½Š°Š¹Š“ŠµŠ½ + + + + + RAM = %ls MB + + RAM = %ls MB + + + + + Value for RAM wasn't found + + Š Š°Š·Š¼ŠµŃ€ RAM Š½Šµ Š½Š°Š¹Š“ŠµŠ½ + + + + + CPUs = %ls + + Š¦ŠŸŠ£ = %ls + + + + + Numbers of CPUs weren't found + + ŠšŠ¾Š»ŠøчŠµŃŃ‚Š²Š¾ Š¦ŠŸŠ£ Š½Šµ Š½Š°Š¹Š“ŠµŠ½Š¾ + + + + + Instance public IP = %ls + + ŠŸŃƒŠ±Š»ŠøчŠ½Ń‹Š¹ IP эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š° = %ls + + + + + Public IP wasn't found + + ŠŸŃƒŠ±Š»ŠøчŠ½Ń‹Š¹ IP Š½Šµ Š½Š°Š¹Š“ŠµŠ½ + + + + + Free-form tags or metadata weren't found + + Š¢ŠµŠ³Šø ŠøŠ»Šø Š¼ŠµŃ‚Š°Š“Š°Š½Š½Ń‹Šµ ŠæрŠ¾ŠøŠ·Š²Š¾Š»ŃŒŠ½Š¾Š¹ фŠ¾Ń€Š¼Ń‹ Š½Šµ Š½Š°Š¹Š“ŠµŠ½Ń‹ + + + + + Cloud-init script wasn't found + + Š”ŠŗрŠøŠæт ŠøŠ½ŠøцŠøŠ°Š»ŠøŠ·Š°Ń†ŠøŠø Š¾Š±Š»Š°ŠŗŠ° Š½Šµ Š½Š°Š¹Š“ŠµŠ½ + + + + + Starting cloud instance with id %s... + + Š—Š°ŠæусŠŗ Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š° с ID %s... + + + + + Starting the cloud instance failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°ŠæустŠøть Š¾Š±Š»Š°Ń‡Š½Ń‹Š¹ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€ + + + + Cloud instance with id %s (provider = '%s', profile = '%s') was started + + ŠžŠ±Š»Š°Ń‡Š½Ń‹Š¹ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€ с ID %s (ŠæрŠ¾Š²Š°Š¹Š“ŠµŃ€ = '%s', ŠæрŠ¾Ń„ŠøŠ»ŃŒ = '%s') Š·Š°ŠæущŠµŠ½ + + + + + Pausing cloud instance with id %s... + + ŠŸŃ€ŠøŠ¾ŃŃ‚Š°Š½Š¾Š²ŠŗŠ° Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š° с ID %s... + + + + + Pause the cloud instance failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠøŠ¾ŃŃ‚Š°Š½Š¾Š²Šøть Š¾Š±Š»Š°Ń‡Š½Ń‹Š¹ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€ + + + + Cloud instance with id %s (provider = '%s', profile = '%s') was paused + + ŠžŠ±Š»Š°Ń‡Š½Ń‹Š¹ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€ с ID %s (ŠæрŠ¾Š²Š°Š¹Š“ŠµŃ€ = '%s', ŠæрŠ¾Ń„ŠøŠ»ŃŒ = '%s') ŠæрŠøŠ¾ŃŃ‚Š°Š½Š¾Š²Š»ŠµŠ½ + + + + + Terminating cloud instance with id %s... + + Š—Š°Š²ŠµŃ€ŃˆŠµŠ½ŠøŠµ рŠ°Š±Š¾Ń‚Ń‹ Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š° с ID %s... + + + + + Termination the cloud instance failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°Š²ŠµŃ€ŃˆŠøть рŠ°Š±Š¾Ń‚Ńƒ Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š° + + + + Cloud instance with id %s (provider = '%s', profile = '%s') was terminated + + ŠžŠ±Š»Š°Ń‡Š½Ń‹Š¹ эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€ с ID %s (ŠæрŠ¾Š²Š°Š¹Š“ŠµŃ€ = '%s', ŠæрŠ¾Ń„ŠøŠ»ŃŒ = '%s') Š·Š°Š²ŠµŃ€ŃˆŠøŠ» рŠ°Š±Š¾Ń‚Ńƒ + + + + + Conflicting parameters: --instance-id and --object-name can't be used together. Choose one. + ŠšŠ¾Š½Ń„Š»ŠøŠŗтующŠøŠµ ŠæŠ°Ń€Š°Š¼ŠµŃ‚ры: --instance-id Šø --object-name Š½Šµ Š¼Š¾Š³ŃƒŃ‚ Š±Ń‹Ń‚ŃŒ ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š½Ń‹ Š²Š¼ŠµŃŃ‚Šµ. Š’Ń‹Š±ŠµŃ€ŠøтŠµ Š¾Š“ŠøŠ½. + + + + Creating cloud image with name '%s' from the instance '%s'... + + Š”Š¾Š·Š“Š°Š½ŠøŠµ Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ Š¾Š±Ń€Š°Š·Š° с ŠøŠ¼ŠµŠ½ŠµŠ¼ '%s' ŠøŠ· эŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€Š° '%s'... + + + + + Creating cloud image with name '%s' from the object '%s' in the bucket '%s'... + + Š”Š¾Š·Š“Š°Š½ŠøŠµ Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ Š¾Š±Ń€Š°Š·Š° с ŠøŠ¼ŠµŠ½ŠµŠ¼ '%s' ŠøŠ· Š¾Š±ŃŠŠµŠŗтŠ° '%s' Š² ŠŗŠ¾Ń€Š·ŠøŠ½Šµ '%s'... + + + + + Creating cloud image failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Š·Š“Š°Ń‚ŃŒ Š¾Š±Š»Š°Ń‡Š½Ń‹Š¹ Š¾Š±Ń€Š°Š· + + + + Cloud image was created successfully + + ŠžŠ±Š»Š°Ń‡Š½Ń‹Š¹ Š¾Š±Ń€Š°Š· сŠ¾Š·Š“Š°Š½ усŠæŠµŃˆŠ½Š¾ + + + + + Duplicate parameter: --bucket-name + Š”ŃƒŠ±Š»ŠøрŠ¾Š²Š°Š½Š½Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€: --bucket-name + + + + Empty parameter: --bucket-name + ŠŸŃƒŃŃ‚Š¾Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€: --bucket-name + + + + Duplicate parameter: --object-name + Š”ŃƒŠ±Š»ŠøрŠ¾Š²Š°Š½Š½Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€: --object-name + + + + Empty parameter: --object-name + ŠŸŃƒŃŃ‚Š¾Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€: --object-name + + + + Duplicate parameter: --display-name + Š”ŃƒŠ±Š»ŠøрŠ¾Š²Š°Š½Š½Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€: --display-name + + + + Empty parameter: --display-name + ŠŸŃƒŃŃ‚Š¾Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€: --display-name + + + + Duplicate parameter: --launch-mode + Š”ŃƒŠ±Š»ŠøрŠ¾Š²Š°Š½Š½Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€: --launch-mode + + + + Empty parameter: --launch-mode + ŠŸŃƒŃŃ‚Š¾Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€: --launch-mode + + + + Missing parameter: --bucket-name + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€: --bucket-name + + + + Exporting image '%s' to the Cloud with name '%s'... + + Š­ŠŗсŠæŠ¾Ń€Ń‚ Š¾Š±Ń€Š°Š·Š° '%s' Š² Š¾Š±Š»Š°ŠŗŠ¾ с ŠøŠ¼ŠµŠ½ŠµŠ¼ '%s'... + + + + + Exporting image '%s' to the Cloud with default name + + Š­ŠŗсŠæŠ¾Ń€Ń‚ Š¾Š±Ń€Š°Š·Š° '%s' Š² Š¾Š±Š»Š°ŠŗŠ¾ с ŠøŠ¼ŠµŠ½ŠµŠ¼ ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю + + + + + Image %s was found + + ŠžŠ±Ń€Š°Š· %s Š½Šµ Š½Š°Š¹Š“ŠµŠ½ + + + + + Process of exporting the image to the Cloud was interrupted. The image wasn't found. + + ŠŸŃ€Š¾Ń†ŠµŃŃ эŠŗсŠæŠ¾Ń€Ń‚Š° Š¾Š±Ń€Š°Š·Š° Š² Š¾Š±Š»Š°ŠŗŠ¾ ŠæрŠµŃ€Š²Š°Š½. ŠžŠ±Ń€Š°Š· Š½Šµ Š½Š°Š¹Š“ŠµŠ½. + + + + + Export the image to the Cloud failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ эŠŗсŠæŠ¾Ń€Ń‚ŠøрŠ¾Š²Š°Ń‚ŃŒ Š¾Š±Ń€Š°Š· Š² Š¾Š±Š»Š°ŠŗŠ¾ + + + + Export the image to the Cloud was successfull + + Š­ŠŗсŠæŠ¾Ń€Ń‚ Š¾Š±Ń€Š°Š·Š° Š² Š¾Š±Š»Š°ŠŗŠ¾ Š·Š°ŠŗŠ¾Š½Ń‡ŠøŠ»ŃŃ усŠæŠµŃˆŠ½Š¾ + + + + + Creating an object '%s' from the cloud image '%s'... + + Š”Š¾Š·Š“Š°Š½ŠøŠµ Š¾Š±ŃŠŠµŠŗтŠ° '%s' ŠøŠ· Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ Š¾Š±Ń€Š°Š·Š° '%s'... + + + + + Cloud image import failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠøŠ¼ŠæŠ¾Ń€Ń‚ŠøрŠ¾Š²Š°Ń‚ŃŒ Š¾Š±Š»Š°Ń‡Š½Ń‹Š¹ Š¾Š±Ń€Š°Š· + + + + Cloud image was imported successfully. Find the downloaded object with the name %s in the system temp folder (find the possible environment variables like TEMP, TMP and etc.) + + Š˜Š¼ŠæŠ¾Ń€Ń‚ Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ Š¾Š±Ń€Š°Š·Š° Š·Š°Š²ŠµŃ€ŃˆŠµŠ½ усŠæŠµŃˆŠ½Š¾. ŠŠ°Š¹Š“ŠøтŠµ Š·Š°Š³Ń€ŃƒŠ¶ŠµŠ½Š½Ń‹Š¹ Š¾Š±ŃŠŠµŠŗт с ŠøŠ¼ŠµŠ½ŠµŠ¼ %s Š² сŠøстŠµŠ¼Š½Š¾Š¹ ŠæŠ°ŠæŠŗŠµ Š“Š»Ń Š²Ń€ŠµŠ¼ŠµŠ½Š½Ń‹Ń… фŠ°Š¹Š»Š¾Š² (ŠæŠ¾ŠøщŠøтŠµ Š²Š¾Š·Š¼Š¾Š¶Š½Ń‹Šµ ŠæŠµŃ€ŠµŠ¼ŠµŠ½Š½Ń‹Šµ Š¾ŠŗруŠ¶ŠµŠ½Šøя Š½Š°ŠæŠ¾Š“Š¾Š±ŠøŠµ TEMP, TMP Šø тŠ°Šŗ Š“Š°Š»ŠµŠµ.) + + + + + Getting information about the cloud image with id '%s'... + + ŠŸŠ¾Š»ŃƒŃ‡ŠµŠ½ŠøŠµ ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŠø Š¾Š± Š¾Š±Š»Š°Ń‡Š½Š¾Š¼ Š¾Š±Ń€Š°Š·Šµ с ID '%s'... + + + + + Reply is in the form 'image property' = 'value' + + ŠžŃ‚Š²ŠµŃ‚ Š² фŠ¾Ń€Š¼Š°Ń‚Šµ 'сŠ²Š¾Š¹ŃŃ‚Š²Š¾ Š¾Š±Ń€Š°Š·Š°' = 'Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ' + + + + + Getting information about the cloud image failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š»ŃƒŃ‡Šøть ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†Šøю Š¾Š± Š¾Š±Š»Š°Ń‡Š½Š¾Š¼ Š¾Š±Ń€Š°Š·Šµ + + + + General information about the image: + + ŠžŠ±Ń‰Š°Ń ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†Šøя Š¾Š± Š¾Š±Ń€Š°Š·Šµ: + + + + + Deleting cloud image with id %s... + + Š£Š“Š°Š»ŠµŠ½ŠøŠµ Š¾Š±Š»Š°Ń‡Š½Š¾Š³Š¾ Š¾Š±Ń€Š°Š·Š° с ID %s... + + + + + Deleting cloud image failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ уŠ“Š°Š»Šøть Š¾Š±Š»Š°Ń‡Š½Ń‹Š¹ Š¾Š±Ń€Š°Š· + + + + Cloud image was deleted successfully + + ŠžŠ±Š»Š°Ń‡Š½Ń‹Š¹ Š¾Š±Ń€Š°Š· уŠ“Š°Š»ŠµŠ½ усŠæŠµŃˆŠ½Š¾ + + + + + + + + Missing --name parameter + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ --name + + + + Missing --network-id parameter + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ --network-id + + + + Cloud network was created successfully + + ŠžŠ±Š»Š°Ń‡Š½Š°Ń сŠµŃ‚ŃŒ сŠ¾Š·Š“Š°Š½Š° усŠæŠµŃˆŠ½Š¾ + + + + + Name: %ls + + Š˜Š¼Ń: %ls + + + + + State: %s + + Š”Š¾ŃŃ‚Š¾ŃŠ½ŠøŠµ: %s + + + + + Enabled + Š’ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾ + + + + Disabled + ŠžŃ‚ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾ + + + + CloudProvider: %ls + + ŠžŠ±Š»Š°Ń‡Š½Ń‹Š¹ ŠæрŠ¾Š²Š°Š¹Š“ŠµŃ€: %ls + + + + + CloudProfile: %ls + + ŠžŠ±Š»Š°Ń‡Š½Ń‹Š¹ ŠæрŠ¾Ń„ŠøŠ»ŃŒ: %ls + + + + + CloudNetworkId: %ls + + ID Š¾Š±Š»Š°Ń‡Š½Š¾Š¹ сŠµŃ‚Šø: %ls + + + + + VBoxNetworkName: %ls + + + Š˜Š¼Ń сŠµŃ‚Šø VBox: %ls + + + + + + Cloud network %ls was updated successfully + + ŠžŠ±Š»Š°Ń‡Š½Š°Ń сŠµŃ‚ŃŒ %ls Š¾Š±Š½Š¾Š²Š»ŠµŠ½Š° усŠæŠµŃˆŠ½Š¾ + + + + + Cloud network %ls was deleted successfully + + ŠžŠ±Š»Š°Ń‡Š½Š°Ń сŠµŃ‚ŃŒ %ls уŠ“Š°Š»ŠµŠ½Š° усŠæŠµŃˆŠ½Š¾ + + + + Failed to compose path to the unattended installer script templates (%Rrc) + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Š±Ń€Š°Ń‚ŃŒ Šæуть Š“Š»Ń шŠ°Š±Š»Š¾Š½Š¾Š² сŠŗрŠøŠæтŠ¾Š² unattended ŠøŠ½ŃŃ‚Š°Š»Š»ŃŃ‚Š¾Ń€Š° (%Rrc) + + + Failed to obtain system properties. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š»ŃƒŃ‡Šøть сŠ²Š¾Š¹ŃŃ‚Š²Š° сŠøстŠµŠ¼Ń‹. + + + Failed to obtain proxy mode. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š»ŃƒŃ‡Šøть рŠµŠ¶ŠøŠ¼ ŠæрŠ¾ŠŗсŠø. + + + Failed to obtain proxy URL. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š»ŃƒŃ‡Šøть URL ŠæрŠ¾ŠŗсŠø. + + + Failed to get system proxy for https://dl.fedoraproject.org. Will use direct connection. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š»ŃƒŃ‡Šøть сŠøстŠµŠ¼Š½Ń‹Š¹ ŠæрŠ¾ŠŗсŠø Š“Š»Ń https://dl.fedoraproject.org. Š‘ŃƒŠ“ŠµŃ‚ ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š½Š¾ ŠæряŠ¼Š¾Šµ сŠ¾ŠµŠ“ŠøŠ½ŠµŠ½ŠøŠµ. + + + Failed to obtain default machine folder. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š»ŃƒŃ‡Šøть ŠæŠ°ŠæŠŗу Š¼Š°ŃˆŠøŠ½Ń‹ ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю. + + + Failed to obtain default guest additions ISO path. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š»ŃƒŃ‡Šøть Šæуть ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю Šŗ ISO Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½ŠøŠ¹ Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ”. + + + The default guest additions ISO path is empty nor it is provided as --guest-additions-iso parameter. Cannot proceed without it. + ŠŸŃƒŃ‚ŃŒ ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю Šŗ ISO Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½ŠøŠ¹ Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ” Šæуст ŠøŠ»Šø Š½Šµ уŠŗŠ°Š·Š°Š½ чŠµŃ€ŠµŠ· --guest-additions-iso parameter. ŠŸŃ€Š¾Š“Š¾Š»Š¶ŠµŠ½ŠøŠµ Š½ŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š±ŠµŠ· этŠ¾Š³Š¾. + + + Failed to compose a path to the local gateway image (%Rrc) + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Š±Ń€Š°Ń‚ŃŒ Šæуть Šŗ Š¾Š±Ń€Š°Š·Ńƒ Š»Š¾ŠŗŠ°Š»ŃŒŠ½Š¾Š³Š¾ шŠ»ŃŽŠ·Š° (%Rrc) + + + Local gateway image already exists, skipping image preparation step. + + ŠžŠ±Ń€Š°Š· Š»Š¾ŠŗŠ°Š»ŃŒŠ½Š¾Š³Š¾ шŠ»ŃŽŠ·Š° уŠ¶Šµ сущŠµŃŃ‚Š²ŃƒŠµŃ‚, ŠæрŠ¾ŠæусŠŗ шŠ°Š³Š° ŠæŠ¾Š“Š³Š¾Ń‚Š¾Š²ŠŗŠø Š¾Š±Ń€Š°Š·Š°. + + + + Preparing unattended install of temporary local gateway machine from %ls... + + ŠŸŠ¾Š“Š³Š¾Ń‚Š¾Š²ŠŗŠ° unattended устŠ°Š½Š¾Š²ŠŗŠø Š¼Š°ŃˆŠøŠ½Ń‹ Š²Ń€ŠµŠ¼ŠµŠ½Š½Š¾Š³Š¾ Š»Š¾ŠŗŠ°Š»ŃŒŠ½Š¾Š³Š¾ шŠ»ŃŽŠ·Š° ŠøŠ· %ls... + + + + Failed to open %ls. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š¾Ń‚Šŗрыть %ls. + + + Failed to create '%ls'. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Š·Š“Š°Ń‚ŃŒ '%ls'. + + + Failed to apply defaults to '%ls'. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠøŠ¼ŠµŠ½Šøть Š½Š°ŃŃ‚Ń€Š¾Š¹ŠŗŠø ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю Šŗ '%ls'. + + + Failed to adjust CPU count for '%ls'. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š“стрŠ¾Šøть ŠŗŠ¾Š»ŠøчŠµŃŃ‚Š²Š¾ Š¦ŠŸŠ£ Š“Š»Ń '%ls'. + + + Failed to adjust memory size for '%ls'. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š“стрŠ¾Šøть рŠ°Š·Š¼ŠµŃ€ ŠæŠ°Š¼ŃŃ‚Šø Š“Š»Ń '%ls'. + + + Failed to set attachment type for the second network adapter. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ устŠ°Š½Š¾Š²Šøть тŠøŠæ ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½Šøя Š“Š»Ń Š²Ń‚Š¾Ń€Š¾Š³Š¾ сŠµŃ‚ŠµŠ²Š¾Š³Š¾ Š°Š“Š°ŠæтŠµŃ€Š°. + + + Failed to disable the audio adapter. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š¾Ń‚ŠŗŠ»ŃŽŃ‡Šøть Š°ŃƒŠ“ŠøŠ¾ Š°Š“Š°ŠæтŠµŃ€. + + + Failed to register '%ls'. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°Ń€ŠµŠ³ŠøстрŠøрŠ¾Š²Š°Ń‚ŃŒ '%ls'. + + + Failed to create %ls. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Š·Š“Š°Ń‚ŃŒ %ls. + + + Failed to create base storage for local gateway image. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Š·Š“Š°Ń‚ŃŒ Š±Š°Š·Š¾Š²Š¾Šµ хрŠ°Š½ŠøŠ»ŠøщŠµ Š“Š»Ń Š¾Š±Ń€Š°Š·Š° Š»Š¾ŠŗŠ°Š»ŃŒŠ½Š¾Š³Š¾ шŠ»ŃŽŠ·Š°. + + + Failed to lock '%ls' for modifications. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°Š±Š»Š¾ŠŗŠøрŠ¾Š²Š°Ń‚ŃŒ '%ls' Š“Š»Ń ŠøŠ·Š¼ŠµŠ½ŠµŠ½Šøя. + + + Failed to obtain a mutable machine. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š»ŃƒŃ‡Šøть ŠøŠ·Š¼ŠµŠ½ŃŠµŠ¼ŃƒŃŽ Š¼Š°ŃˆŠøŠ½Ńƒ. + + + Failed to attach HD to '%ls'. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡Šøть Š¶ŠµŃŃ‚ŠŗŠøŠ¹ Š“ŠøсŠŗ Šŗ '%ls'. + + + Failed to attach ISO to '%ls'. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡Šøть ISO Šŗ '%ls'. + + + Failed to save '%ls' settings. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Ń…Ń€Š°Š½Šøть Š½Š°ŃŃ‚Ń€Š¾Š¹ŠŗŠø '%ls'. + + + Failed to create unattended installer. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Š·Š“Š°Ń‚ŃŒ unattended ŠøŠ½ŃŃ‚Š°Š»Š»ŃŃ‚Š¾Ń€. + + + Failed to set machine for the unattended installer. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°Š“Š°Ń‚ŃŒ Š¼Š°ŃˆŠøŠ½Ńƒ Š“Š»Ń unattended ŠøŠ½ŃŃ‚Š°Š»Š»ŃŃ‚Š¾Ń€Š°. + + + Failed to set user for the unattended installer. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°Š“Š°Ń‚ŃŒ ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Ń Š“Š»Ń unattended ŠøŠ½ŃŃ‚Š°Š»Š»ŃŃ‚Š¾Ń€Š°. + + + Failed to set password for the unattended installer. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°Š“Š°Ń‚ŃŒ ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Ń Š“Š»Ń unattended ŠøŠ½ŃŃ‚Š°Š»Š»ŃŃ‚Š¾Ń€Š°. + + + Failed to set full user name for the unattended installer. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°Š“Š°Ń‚ŃŒ ŠæŠ¾Š»Š½Š¾Šµ ŠøŠ¼Ń ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Ń Š“Š»Ń unattended ŠøŠ½ŃŃ‚Š°Š»Š»ŃŃ‚Š¾Ń€Š°. + + + Failed to enable guest addtions for the unattended installer. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š²ŠŗŠ»ŃŽŃ‡Šøть Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½Šøя Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ” Š“Š»Ń unattended ŠøŠ½ŃŃ‚Š°Š»Š»ŃŃ‚Š¾Ń€Š°. + + + Failed to set guest addtions ISO path for the unattended installer. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°Š“Š°Ń‚ŃŒ Šæуть Šŗ Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½ŠøяŠ¼ Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ” Š“Š»Ń unattended ŠøŠ½ŃŃ‚Š°Š»Š»ŃŃ‚Š¾Ń€Š°. + + + Failed to set script template for the unattended installer. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°Š“Š°Ń‚ŃŒ шŠ°Š±Š»Š¾Š½ сŠŗрŠøŠæтŠ° Š“Š»Ń unattended ŠøŠ½ŃŃ‚Š°Š»Š»ŃŃ‚Š¾Ń€Š°. + + + Failed to set post install script template for the unattended installer. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°Š“Š°Ń‚ŃŒ шŠ°Š»Š¾Š½ post-install сŠŗрŠøŠæтŠ° Š“Š»Ń unattended ŠøŠ½ŃŃ‚Š°Š»Š»ŃŃ‚Š¾Ń€Š°. + + + Failed to prepare unattended installation. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š“Š³Š¾Ń‚Š¾Š²Šøть unattended устŠ°Š½Š¾Š²Šŗу. + + + Failed to construct media for unattended installation. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Š±Ń€Š°Ń‚ŃŒ Š½Š¾ŃŠøтŠµŠ»ŃŒ Š“Š»Ń unattended устŠ°Š½Š¾Š²ŠŗŠø. + + + Failed to reconfigure %ls for unattended installation. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠµŃ€ŠµŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€ŠøрŠ¾Š²Š°Ń‚ŃŒ %ls Š“Š»Ń unattended устŠ°Š½Š¾Š²ŠŗŠø. + + + %32s = failed: %Rhrc + + %32s = Š·Š°Š²ŠµŃ€ŃˆŠµŠ½Š¾ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹: %Rhrc + + + + Performing unattended install of temporary local gateway... + + ŠŸŃ€Š¾ŠøŠ·Š²Š¾Š“Šøтся unattended устŠ°Š½Š¾Š²ŠŗŠ° Š²Ń€ŠµŠ¼ŠµŠ½Š½Š¾Š³Š¾ Š»Š¾ŠŗŠ°Š»ŃŒŠ½Š¾Š³Š¾ шŠ»ŃŽŠ·Š°... + + + + Failed to launch '%ls'. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°ŠæустŠøть '%ls'. + + + Failed to get machine state. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š»ŃƒŃ‡Šøть сŠ¾ŃŃ‚Š¾ŃŠ½ŠøŠµ Š¼Š°ŃˆŠøŠ½Ń‹. + + + Temporary local gateway VM has aborted. + Š’Šœ Š²Ń€ŠµŠ¼ŠµŠ½Š½Š¾Š³Š¾ Š»Š¾ŠŗŠ°Š»ŃŒŠ½Š¾Š³Š¾ шŠ»ŃŽŠ·Š° ŠæрŠµŃ€Š²Š°Š½Š°. + + + Timed out (40min) while waiting for unattended install to finish. + ŠŸŃ€ŠµŠ²Ń‹ŃˆŠµŠ½Š¾ Š²Ń€ŠµŠ¼Ń Š¾Š¶ŠøŠ“Š°Š½Šøя (40 Š¼ŠøŠ½) Š·Š°Š²ŠµŃ€ŃˆŠµŠ½Šøя unattended устŠ°Š½Š¾Š²ŠŗŠø. + + + Done. + + Š”Š“ŠµŠ»Š°Š½Š¾. + + + + Detaching local gateway image... + + ŠžŃ‚ŠŗŠ»ŃŽŃ‡ŠµŠ½ŠøŠµ Š¾Š±Ń€Š°Š·Š° Š»Š¾ŠŗŠ°Š»ŃŒŠ½Š¾Š³Š¾ шŠ»ŃŽŠ·Š°... + + + + Failed to detach HD to '%ls'. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š¾Ń‚ŠŗŠ»ŃŽŃ‡Šøть Š¶ŠµŃŃ‚ŠŗŠøŠ¹ Š“ŠøсŠŗ Š¾Ń‚ '%ls'. + + + Unregistering temporary local gateway machine... + + ŠžŃ‚Š¼ŠµŠ½Š° рŠµŠ³ŠøстрŠ°Ń†ŠøŠø Š¼Š°ŃˆŠøŠ½Ń‹ Š²Ń€ŠµŠ¼ŠµŠ½Š½Š¾Š³Š¾ Š»Š¾ŠŗŠ°Š»ŃŒŠ½Š¾Š³Š¾ шŠ»ŃŽŠ·Š°... + + + + Failed to unregister '%ls'. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š¾Ń‚Š¼ŠµŠ½Šøть рŠµŠ³ŠøстрŠ°Ń†Šøю '%ls'. + + + Failed to delete config for '%ls'. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ уŠ“Š°Š»Šøть ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†Šøю Š“Š»Ń '%ls'. + + + Making local gateway image immutable... + + ŠŸŃ€ŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Š½ŠøŠµ Š¾Š±Ń€Š°Š·Š° Š»Š¾ŠŗŠ°Š»ŃŒŠ½Š¾Š³Š¾ шŠ»ŃŽŠ·Š° Š² Š½ŠµŠøŠ·Š¼ŠµŠ½ŃŠµŠ¼Ń‹Š¹... + + + + Failed to open '%ls'. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š¾Ń‚Šŗрыть '%ls'. + + + Failed to make '%ls' immutable. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ“ŠµŠ»Š°Ń‚ŃŒ '%ls' Š½ŠµŠøŠ·Š¼ŠµŠ½ŃŠµŠ¼Ń‹Š¼. + + + Missing --local-gateway-iso parameter + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ --local-gateway-iso + + + + Setting up tunnel network in the cloud... + + Š£ŃŃ‚Š°Š½Š¾Š²ŠŗŠ° туŠ½Š½ŠµŠ»ŃŒŠ½Š¾Š¹ сŠµŃ‚Šø Š² Š¾Š±Š»Š°ŠŗŠµ... + + + + + Setting up cloud network environment failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ устŠ°Š½Š¾Š²Šøть Š¾ŠŗруŠ¶ŠµŠ½ŠøŠµ Š¾Š±Š»Š°Ń‡Š½Š¾Š¹ сŠµŃ‚Šø + + + + Cloud network environment was set up successfully. Tunnel network id is: %ls + + ŠžŠŗруŠ¶ŠµŠ½ŠøŠµ Š¾Š±Š»Š°Ń‡Š½Š¾Š¹ сŠµŃ‚Šø устŠ°Š½Š¾Š²Š»ŠµŠ½Š¾ усŠæŠµŃˆŠ½Š¾. ID туŠ½Š½ŠµŠ»ŃŒŠ½Š¾Š¹ сŠµŃ‚Šø: %ls + + + + + CloudMachine + + + cloud: no providers available + Š¾Š±Š»Š°ŠŗŠ¾: Š½ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠ½Š¾ Š½Šø Š¾Š“Š½Š¾Š³Š¾ ŠæрŠ¾Š²Š°Š¹Š“ŠµŃ€Š° + + + + cloud: multiple providers available, '--provider' option is required + Š¾Š±Š»Š°ŠŗŠ¾: Š“Š¾ŃŃ‚ŃƒŠæŠ½Š¾ Š½ŠµŃŠŗŠ¾Š»ŃŒŠŗŠ¾ ŠæрŠ¾Š²Š°Š¹Š“ŠµŃ€Š¾Š², трŠµŠ±ŃƒŠµŃ‚ся Š¾ŠæцŠøя '--provider' + + + + cloud: no profiles exist + Š¾Š±Š»Š°ŠŗŠ¾: Š½Šµ сущŠµŃŃ‚Š²ŃƒŠµŃ‚ Š½Šø Š¾Š“Š½Š¾Š³Š¾ ŠæрŠ¾Ń„ŠøŠ»Ń + + + + cloud: multiple profiles exist, '--profile' option is required + Š¾Š±Š»Š°ŠŗŠ¾: сущŠµŃŃ‚Š²ŃƒŠµŃ‚ Š½ŠµŃŠŗŠ¾Š»ŃŒŠŗŠ¾ ŠæрŠ¾Ń„ŠøŠ»ŠµŠ¹, трŠµŠ±ŃƒŠµŃ‚ся Š¾ŠæцŠøя '--profile' + + + + ambiguous name: %ls and %ls + Š½ŠµŠ¾Š“Š½Š¾Š·Š½Š°Ń‡Š½Š¾Šµ ŠøŠ¼Ń: %ls Šø %ls + + + + only one machine can be specified + Š¼Š¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ уŠŗŠ°Š·Š°Š½Š° тŠ¾Š»ŃŒŠŗŠ¾ Š¾Š“Š½Š° Š¼Š°ŃˆŠøŠ½Š° + + + + not a valid uuid: %s + Š½ŠµŠ“ŠµŠ¹ŃŃ‚Š²ŠøтŠµŠ»ŃŒŠ½Ń‹Š¹ uuid: %s + + + + machine not specified + Š¼Š°ŃˆŠøŠ½Š° Š½Šµ уŠŗŠ°Š·Š°Š½Š° + + + + machine name is empty + ŠøŠ¼Ń Š¼Š°ŃˆŠøŠ½Ń‹ ŠæустŠ¾ + + + + unable to find machine with id %s + Š½ŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š½Š°Š¹Ń‚Šø Š¼Š°ŃˆŠøŠ½Ńƒ с ID %s + + + + unable to find machine with name %s + Š½ŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š½Š°Š¹Ń‚Šø Š¼Š°ŃˆŠøŠ½Ńƒ с ŠøŠ¼ŠµŠ½ŠµŠ¼ %s + + + + unable to find machine %s + Š½ŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š½Š°Š¹Ń‚Šø Š¼Š°ŃˆŠøŠ½Ńƒ %s + + + + cloud machine: RTGetOptInit: %Rra + Š¾Š±Š»Š°Ń‡Š½Š°Ń Š¼Š°ŃˆŠøŠ½Š°: RTGetOptInit: %Rra + + + + + Invalid sub-command: %s + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š°Ń ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“Š°: %s + + + + cloud machine: internal error: %d + Š¾Š±Š»Š°Ń‡Š½Š°Ń Š¼Š°ŃˆŠøŠ½Š°: Š²Š½ŃƒŃ‚Ń€ŠµŠ½Š½ŃŃ Š¾ŃˆŠøŠ±ŠŗŠ°: %d + + + + cloud machine: command required +Try '--help' for more information. + Š¾Š±Š»Š°Ń‡Š½Š°Ń Š¼Š°ŃˆŠøŠ½Š°: трŠµŠ±ŃƒŠµŃ‚ся ŠŗŠ¾Š¼Š°Š½Š“Š° +Š˜ŃŠæŠ¾Š»ŃŒŠ·ŃƒŠ¹Ń‚Šµ '--help' Š“Š»Ń Š“ŠµŃ‚Š°Š»ŃŒŠ½Š¾Š¹ ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŠø. + + + + cloud machine list: unexpected machine argument + сŠæŠøсŠ¾Šŗ Š¾Š±Š»Š°Ń‡Š½Ń‹Ń… Š¼Š°ŃˆŠøŠ½: Š½ŠµŠ¾Š¶ŠøŠ“Š°Š½Š½Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Š¼Š°ŃˆŠøŠ½Ń‹ + + + + cloud machine list: RTGetOptInit: %Rra + сŠæŠøсŠ¾Šŗ Š¾Š±Š»Š°Ń‡Š½Ń‹Ń… Š¼Š°ŃˆŠøŠ½: RTGetOptInit: %Rra + + + + machine is not accessible + Š¼Š°ŃˆŠøŠ½Š° Š½ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠ½Š° + + + + State: Invalid (%RU32) + + Š”Š¾ŃŃ‚Š¾ŃŠ½ŠøŠµ: ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ (%RU32) + + + + + State: Provisioning (%RU32) + + Š”Š¾ŃŃ‚Š¾ŃŠ½ŠøŠµ: ŠŸŠ¾Š“Š³Š¾Ń‚Š¾Š²ŠŗŠ° (%RU32) + + + + + State: Running (%RU32) + + Š”Š¾ŃŃ‚Š¾ŃŠ½ŠøŠµ: Š Š°Š±Š¾Ń‚Š°ŠµŃ‚ (%RU32) + + + + + State: Starting (%RU32) + + Š”Š¾ŃŃ‚Š¾ŃŠ½ŠøŠµ: Š—Š°ŠæусŠŗŠ°ŠµŃ‚ся (%RU32) + + + + + State: Stopping (%RU32) + + Š”Š¾ŃŃ‚Š¾ŃŠ½ŠøŠµ: ŠžŃŃ‚Š°Š½Š°Š²Š»ŠøŠ²Š°ŠµŃ‚ся (%RU32) + + + + + State: Stopped (%RU32) + + Š”Š¾ŃŃ‚Š¾ŃŠ½ŠøŠµ: ŠžŃŃ‚Š°Š½Š¾Š²Š»ŠµŠ½Š° (%RU32) + + + + + State: CreatingImage (%RU32) + + Š”Š¾ŃŃ‚Š¾ŃŠ½ŠøŠµ: Š”Š¾Š·Š“Š°Š½ŠøŠµ Š¾Š±Ń€Š°Š·Š° (%RU32) + + + + + State: Terminating (%RU32) + + Š”Š¾ŃŃ‚Š¾ŃŠ½ŠøŠµ: Š—Š°Š²ŠµŃ€ŃˆŠµŠ½ŠøŠµ (%RU32) + + + + + State: Terminated (%RU32) + + Š”Š¾ŃŃ‚Š¾ŃŠ½ŠøŠµ: Š—Š°Š²ŠµŃ€ŃˆŠµŠ½Š¾ (%RU32) + + + + + State: Unknown state (%RU32) + + Š”Š¾ŃŃ‚Š¾ŃŠ½ŠøŠµ: ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Š¾Šµ сŠ¾ŃŃ‚Š¾ŃŠ½ŠøŠµ (%RU32) + + + + + null details + ŠæŠ¾Š“рŠ¾Š±Š½Š°Ń ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†Šøя Š¾Ń‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ + + + + %ls: unable to convert to boolean value + + %ls: Š½ŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæрŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Ń‚ŃŒ Š² Š±ŃƒŠ»ŠµŠ²Š¾ Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ + + + + + %ls: unable to convert to string value + + %ls: Š½ŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæрŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Ń‚ŃŒ Š² стрŠ¾ŠŗŠ¾Š²Š¾Šµ Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ + + + + + %ls: unable to convert to integer value + + %ls: Š½ŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæрŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Ń‚ŃŒ Š² цŠµŠ»Š¾Ń‡ŠøсŠ»ŠµŠ½Š½Š¾Šµ Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ + + + + + %ls: unable to convert to choice value + + %ls: Š½ŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæрŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Ń‚ŃŒ Š² Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ Š²Ń‹Š±Š¾Ń€Š° + + + + + %ls: values: %Rhra + %ls: Š·Š½Š°Ń‡ŠµŠ½Šøя: %Rhra + + + + %ls: selectedIndex: %Rhra + %ls: Š²Ń‹Š±Ń€Š°Š½Š½Ń‹Š¹ ŠøŠ½Š“ŠµŠŗс: %Rhra + + + + %ls: selected index %RI64 out of range [0, %zu) + + %ls: Š²Ń‹Š±Ń€Š°Š½Š½Ń‹Š¹ ŠøŠ½Š“ŠµŠŗс %RI64 Š²Š½Šµ Š³Ń€Š°Š½Šøц Š“ŠøŠ°ŠæŠ°Š·Š¾Š½Š° [0, %zu) + + + + + unknown value type %RU32 + + Š½ŠµŠøŠ·Š²ŠµŃŃ‚Š½Ń‹Š¹ тŠøŠæ Š·Š½Š°Ń‡ŠµŠ½Šøя %RU32 + + + + + CloudVM + + Invalid %s number '%s' + ŠŠµŠ“ŠµŠ¹ŃŃ‚Š²ŠøтŠµŠ»ŃŒŠ½Ń‹Š¹ Š½Š¾Š¼ŠµŃ€ %s '%s' + + + Failed to send a scancode + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾ŃŠ»Š°Ń‚ŃŒ сŠŗŠ°Š½ŠŗŠ¾Š“ + + + Only %d scancodes were stored + + Š”Š¾Ń…Ń€Š°Š½ŠµŠ½ тŠ¾Š»ŃŒŠŗŠ¾ %d сŠŗŠ°Š½ŠŗŠ¾Š“ + Š”Š¾Ń…Ń€Š°Š½ŠµŠ½Š¾ тŠ¾Š»ŃŒŠŗŠ¾ %d сŠŗŠ°Š½ŠŗŠ¾Š“Š° + Š”Š¾Ń…Ń€Š°Š½ŠµŠ½Š¾ тŠ¾Š»ŃŒŠŗŠ¾ %d сŠŗŠ°Š½ŠŗŠ¾Š“Š¾Š² + + + + Out of memory allocating %d bytes + + ŠŠµ хŠ²Š°Ń‚Š°ŠµŃ‚ ŠæŠ°Š¼ŃŃ‚Šø ŠæŠ¾Š“ Š±ŃƒŃ„ŠµŃ€ %d Š±Š°Š¹Ń‚ + ŠŠµ хŠ²Š°Ń‚Š°ŠµŃ‚ ŠæŠ°Š¼ŃŃ‚Šø ŠæŠ¾Š“ Š±ŃƒŃ„ŠµŃ€ %d Š±Š°Š¹Ń‚Š° + ŠŠµ хŠ²Š°Ń‚Š°ŠµŃ‚ ŠæŠ°Š¼ŃŃ‚Šø ŠæŠ¾Š“ Š±ŃƒŃ„ŠµŃ€ %d Š±Š°Š¹Ń‚ + + + + File size %RI64 is greater than %RI64: '%s' + Š Š°Š·Š¼ŠµŃ€ фŠ°Š¹Š»Š° %RI64 Š±Š¾Š»ŃŒŃˆŠµ чŠµŠ¼ %RI64: '%s' + + + Cannot get size of file '%s': %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæŠ¾Š»ŃƒŃ‡Šøть рŠ°Š·Š¼ŠµŃ€ фŠ°Š¹Š»Š° '%s': %Rrc + + + Cannot open file '%s': %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š¾Ń‚Šŗрыть фŠ°Š¹Š» '%s': %Rrc + + + Not enough parameters + ŠŠµŠ“Š¾ŃŃ‚Š°Ń‚Š¾Ń‡Š½Š¾ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š¾Š² + + + Machine '%s' is not currently running + ŠœŠ°ŃˆŠøŠ½Š° '%s' сŠµŠ¹Ń‡Š°Ń Š½Šµ Š·Š°ŠæущŠµŠ½Š° + + + Missing argument to '%s'. Expected CPU number. + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s'. ŠžŠ¶ŠøŠ“Š°ŠµŃ‚ся Š½Š¾Š¼ŠµŃ€ Š¦ŠŸŠ£. + + + Missing argument to '%s'. Expected execution cap number. + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s'. ŠžŠ¶ŠøŠ“Š°ŠµŃ‚ся %% Š²Ń€ŠµŠ¼ŠµŠ½Šø Š² %% Š²Ń‹Š“ŠµŠ»ŠµŠ½Š½Š¾Š³Š¾ Š“Š»Ń Š²Ń‹ŠæŠ¾Š»Š½ŠµŠ½Šøя. + + + Invalid value '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ '%s' + + + audio adapter not enabled in VM configuration + Š°ŃƒŠ“ŠøŠ¾ Š°Š“Š°ŠæтŠµŃ€ Š½Šµ Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½ Š² ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†ŠøŠø Š’Šœ + + + Missing argument to '%s'. + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s'. + + + Invalid '%s' argument '%s'. + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s' '%s'. + + + Missing argument to '%s'. Expected enabled / disabled. + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s'. ŠžŠ¶ŠøŠ“Š°ŠµŃ‚ся enabled / disabled. + + + Missing argument to '%s'. Expected drag and drop mode. + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s'. ŠžŠ¶ŠøŠ“Š°ŠµŃ‚ся рŠµŠ¶ŠøŠ¼ drag and drop. + + + Failed to power off machine + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š²Ń‹ŠŗŠ»ŃŽŃ‡Šøть Š¼Š°ŃˆŠøŠ½Ńƒ + + + Machine in invalid state %d -- %s + + ŠœŠ°ŃˆŠøŠ½Š° Š² Š½ŠµŠ“Š¾ŠæустŠøŠ¼Š¾Š¼ сŠ¾ŃŃ‚Š¾ŃŠ½ŠøŠø %d -- %s + + + + Failed to save machine state + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Ń…Ń€Š°Š½Šøть сŠ¾ŃŃ‚Š¾ŃŠ½ŠøŠµ Š¼Š°ŃˆŠøŠ½Ń‹ + + + Guest not running + Š“Š¾ŃŃ‚ŠµŠ²Š°Ń сŠøстŠµŠ¼Š° Š½Šµ Š·Š°ŠæущŠµŠ½Š° + + + Current installed Guest Additions don't support %s the guest. + Š¢ŠµŠŗущŠøŠµ устŠ°Š½Š¾Š²Š»ŠµŠ½Š½Ń‹Šµ Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½Šøя Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ” Š½Šµ ŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŃŽŃ‚ %s Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ сŠøстŠµŠ¼Ń‹. + + + rebooting + ŠæŠµŃ€ŠµŠ·Š°Š³Ń€ŃƒŠ·Šŗу + + + shutting down + Š·Š°Š²ŠµŃ€ŃˆŠµŠ½ŠøŠµ + + + Missing argument to '%s'. Expected IBM PC AT set 2 keyboard scancode(s) as hex byte(s). + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s'. ŠžŠ¶ŠøŠ“Š°ŃŽŃ‚ся сŠŗŠ°Š½ŠŗŠ¾Š“ы ŠŗŠ»Š°Š²ŠøŠ°Ń‚ŃƒŃ€Ń‹ IBM PC AT set 2 Š² Š²ŠøŠ“Šµ hex Š±Š°Š¹Ń‚Š¾Š². + + + Converting '%s' returned %Rrc! + ŠŸŃ€ŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Š½ŠøŠµ '%s' Š²ŠµŃ€Š½ŃƒŠ»Š¾ %Rrc! + + + Error: '%s' is not a hex byte! + ŠžŃˆŠøŠ±ŠŗŠ°: '%s' Š½Šµ яŠ²Š»ŃŠµŃ‚ся hex Š±Š°Š¹Ń‚Š¾Š¼! + + + Missing argument to '%s'. Expected ASCII string(s). + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s'. ŠžŠ¶ŠøŠ“Š°ŠµŃ‚ся стрŠ¾ŠŗŠ° ASCII. + + + Missing argument to '%s'. Expected file name. + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s'. ŠžŠ¶ŠøŠ“Š°ŠµŃ‚ся ŠøŠ¼Ń фŠ°Š¹Š»Š°. + + + Missing argument to '%s' + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s' + + + Invalid link state '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ сŠ¾ŃŃ‚Š¾ŃŠ½ŠøŠµ ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½Šøя '%s' + + + Invalid filename or filename not specified for NIC %lu + ŠŠµŠ“ŠµŠ¹ŃŃ‚Š²ŠøтŠµŠ»ŃŒŠ½Š¾Šµ ŠøŠ¼Ń фŠ°Š¹Š»Š° ŠøŠ»Šø ŠøŠ¼Ń фŠ°Š¹Š»Š° Š½Šµ уŠŗŠ°Š·Š°Š½Š¾ Š“Š»Ń сŠµŃ‚ŠµŠ²Š¾Š³Š¾ Š°Š“Š°ŠæтŠµŃ€Š° %lu + + + The NIC %d is currently disabled and thus its tracefile can't be changed + NIC %d Š² Š½Š°ŃŃ‚Š¾ŃŃ‰ŠµŠµ Š²Ń€ŠµŠ¼Ń Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½ Šø ŠæŠ¾ŃŃ‚Š¾Š¼Ńƒ ŠµŠ³Š¾ трŠ°ŃŃŠøрŠ¾Š²Š¾Ń‡Š½Ń‹Š¹ фŠ°Š¹Š» Š½Šµ Š¼Š¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ ŠøŠ·Š¼ŠµŠ½ŠµŠ½ + + + Invalid nictrace%lu argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ nictrace%lu '%s' + + + The NIC %d is currently disabled and thus its trace flag can't be changed + NIC %d Š² Š½Š°ŃŃ‚Š¾ŃŃ‰ŠµŠµ Š²Ń€ŠµŠ¼Ń Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½ Šø ŠæŠ¾ŃŃ‚Š¾Š¼Ńƒ ŠµŠ³Š¾ трŠ°ŃŃŠøрŠ¾Š²Š¾Ń‡Š½Ń‹Š¹ фŠ»Š°Š³ Š½Šµ Š¼Š¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ ŠøŠ·Š¼ŠµŠ½ŠµŠ½ + + + Missing or invalid argument to '%s' + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ ŠøŠ»Šø Š½ŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s' + + + Wrong rule proto '%s' specified -- only 'udp' and 'tcp' are allowed. + Š£ŠŗŠ°Š·Š°Š½ Š½ŠµŠæрŠ°Š²ŠøŠ»ŃŒŠ½Ń‹Š¹ ŠæрŠ¾Ń‚Š¾ŠŗŠ¾Š» ŠæрŠ°Š²ŠøŠ»Š° '%s' -- рŠ°Š·Ń€ŠµŃˆŠµŠ½Ń‹ тŠ¾Š»ŃŒŠŗŠ¾ 'udp' Šø 'tcp'. + + + Invalid nicproperty%d argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ nicproperty%d '%s' + + + Error: Failed to allocate memory for nicproperty%d '%s' + + ŠžŃˆŠøŠ±ŠŗŠ°: ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š²Ń‹Š“ŠµŠ»Šøть ŠæŠ°Š¼ŃŃ‚ŃŒ Š“Š»Ń nicproperty%d '%s' + + + + The NIC %d is currently disabled and thus its properties can't be changed + NIC %d Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½ Š² Š½Š°ŃŃ‚Š¾ŃŃ‰ŠµŠµ Š²Ń€ŠµŠ¼Ń, Šø ŠæŠ¾ŃŃ‚Š¾Š¼Ńƒ Š½ŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠøŠ·Š¼ŠµŠ½Šøть ŠµŠ³Š¾ сŠ²Š¾Š¹ŃŃ‚Š²Š° + + + Unknown promiscuous mode policy '%s' + ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Š°Ń ŠæŠ¾Š»ŠøтŠøŠŗŠ° Š½ŠµŃ€Š°Š·Š±Š¾Ń€Ń‡ŠøŠ²Š¾Š³Š¾ рŠµŠ¶ŠøŠ¼Š° '%s' + + + The NIC %d is currently disabled and thus its promiscuous mode can't be changed + NIC %d Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½ Š² Š½Š°ŃŃ‚Š¾ŃŃ‰ŠµŠµ Š²Ń€ŠµŠ¼Ń, Šø ŠæŠ¾ŃŃ‚Š¾Š¼Ńƒ Š½ŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠøŠ·Š¼ŠµŠ½Šøть Š½ŠµŃ€Š°Š·Š±Š¾Ń€Ń‡ŠøŠ²Ń‹Š¹ рŠµŠ¶ŠøŠ¼ + + + Invalid type '%s' specfied for NIC %lu + Š£ŠŗŠ°Š·Š°Š½ Š½ŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ тŠøŠæ '%s' Š“Š»Ń NIC %lu + + + The NIC %d is currently disabled and thus its attachment type can't be changed + NIC %d Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½ Š² Š½Š°ŃŃ‚Š¾ŃŃ‰ŠµŠµ Š²Ń€ŠµŠ¼Ń, Šø ŠæŠ¾ŃŃ‚Š¾Š¼Ńƒ Š½ŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠøŠ·Š¼ŠµŠ½Šøть тŠøŠæ ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½Šøя + + + Warning: 'vrdp' is deprecated. Use 'vrde'. + + ŠŸŃ€ŠµŠ“уŠæрŠµŠ¶Š“ŠµŠ½ŠøŠµ: 'vrdp' устŠ°Ń€ŠµŠ». Š˜ŃŠæŠ¾Š»ŃŒŠ·ŃƒŠ¹Ń‚Šµ 'vrde'. + + + + Invalid remote desktop server state '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ сŠ¾ŃŃ‚Š¾ŃŠ½ŠøŠµ сŠµŃ€Š²ŠµŃ€Š° уŠ“Š°Š»ŠµŠ½Š½Š¾Š³Š¾ рŠ°Š±Š¾Ń‡ŠµŠ³Š¾ стŠ¾Š»Š° '%s' + + + Warning: 'vrdpport' is deprecated. Use 'vrdeport'. + + ŠŸŃ€ŠµŠ“уŠæрŠµŠ¶Š“ŠµŠ½ŠøŠµ: 'vrdpport' устŠ°Ń€ŠµŠ». Š˜ŃŠæŠ¾Š»ŃŒŠ·ŃƒŠ¹Ń‚Šµ 'vrdeport'. + + + + Warning: 'vrdpvideochannelquality' is deprecated. Use 'vrdevideochannelquality'. + + ŠŸŃ€ŠµŠ“уŠæрŠµŠ¶Š“ŠµŠ½ŠøŠµ: 'vrdpvideochannelquality' устŠ°Ń€ŠµŠ». Š˜ŃŠæŠ¾Š»ŃŒŠ·ŃƒŠ¹Ń‚Šµ 'vrdevideochannelquality'. + + + + Invalid vrdeproperty argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ vrdeproperty '%s' + + + Error: Failed to allocate memory for VRDE property '%s' + + ŠžŃˆŠøŠ±ŠŗŠ°: ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š²Ń‹Š“ŠµŠ»Šøть ŠæŠ°Š¼ŃŃ‚ŃŒ Š“Š»Ń сŠ²Š¾Š¹ŃŃ‚Š²Š° VRDE '%s' + + + + Wrong number of arguments + ŠŠµŠæрŠ°Š²ŠøŠ»ŃŒŠ½Š¾Šµ ŠŗŠ¾Š»ŠøчŠµŃŃ‚Š²Š¾ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚Š¾Š² + + + Invalid parameter '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ '%s' + + + Zero UUID argument '%s' + ŠŃƒŠ»ŠµŠ²Š¾Š¹ UUID Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ '%s' + + + Incorrect number of parameters + ŠŠµŠŗŠ¾Ń€Ń€ŠµŠŗтŠ½Š¾Šµ ŠŗŠ¾Š»ŠøчŠµŃŃ‚Š²Š¾ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š¾Š² + + + Either "yes" or "no" is expected + ŠžŠ¶ŠøŠ“Š°ŠµŃ‚ся тŠ¾Š»ŃŒŠŗŠ¾ "yes" ŠøŠ»Šø "no" + + + Display status must be <on> or <off> + Š”тŠ°Ń‚ŃƒŃ эŠŗрŠ°Š½Š° Š“Š¾Š»Š¶ŠµŠ½ Š±Ń‹Ń‚ŃŒ <on> ŠøŠ»Šø <off> + + + Invalid host DVD drive name "%s" + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ ŠøŠ¼Ń DVD Š“ŠøсŠŗŠ¾Š²Š¾Š“Š° хŠ¾ŃŃ‚Š° "%s" + + + IDE Controller + IDE ŠšŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€ + + + Invalid host floppy drive name "%s" + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ ŠøŠ¼Ń фŠ»Š¾ŠæŠæŠø Š“ŠøсŠŗŠ¾Š²Š¾Š“Š° хŠ¾ŃŃ‚Š° "%s" + + + Floppy Controller + Š¤Š»Š¾ŠæŠæŠø ŠšŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€ + + + Error parsing guest memory balloon size '%s' + ŠžŃˆŠøŠ±ŠŗŠ° рŠ°Š·Š±Š¾Ń€Š° рŠ°Š·Š¼ŠµŃ€Š° balloon ŠæŠ°Š¼ŃŃ‚Šø Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ сŠøстŠµŠ¼Ń‹ '%s' + + + Teleportation failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾ŠøŠ·Š²ŠµŃŃ‚Šø ŠæŠ¾Ń€Ń‚ŠøрŠ¾Š²Š°Š½ŠøŠµ + + + Error parsing display number '%s' + ŠžŃˆŠøŠ±ŠŗŠ° рŠ°Š·Š±Š¾Ń€Š° Š½Š¾Š¼ŠµŃ€Š° эŠŗрŠ°Š½Š° '%s' + + + Failed to create file '%s' (%Rrc) + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Š·Š“Š°Ń‚ŃŒ фŠ°Š¹Š» '%s' (%Rrc) + + + Failed to write screenshot to file '%s' (%Rrc) + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°ŠæŠøсŠ°Ń‚ŃŒ сŠ½ŠøŠ¼Š¾Šŗ эŠŗрŠ°Š½Š° Š² фŠ°Š¹Š» '%s' (%Rrc) + + + Invalid screen ID specified '%u' + Š£ŠŗŠ°Š·Š°Š½ Š½ŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ID эŠŗрŠ°Š½Š° '%u' + + + Error parsing video width '%s' + ŠžŃˆŠøŠ±ŠŗŠ° ŠæрŠø рŠ°Š·Š±Š¾Ń€Šµ шŠøрŠøŠ½Ń‹ Š²ŠøŠ“ŠµŠ¾ '%s' + + + Error parsing video height '%s' + ŠžŃˆŠøŠ±ŠŗŠ° ŠæрŠø рŠ°Š·Š±Š¾Ń€Šµ Š²Ń‹ŃŠ¾Ń‚Ń‹ Š²ŠøŠ“ŠµŠ¾ '%s' + + + Error parsing video rate '%s' + ŠžŃˆŠøŠ±ŠŗŠ° ŠæрŠø рŠ°Š·Š±Š¾Ń€Šµ Š±ŠøтрŠµŠ¹Ń‚Š° Š²ŠøŠ“ŠµŠ¾ '%s' + + + Error parsing video FPS '%s' + ŠžŃˆŠøŠ±ŠŗŠ° ŠæрŠø рŠ°Š·Š±Š¾Ń€Šµ FPS Š²ŠøŠ“ŠµŠ¾ '%s' + + + Error parsing maximum time '%s' + ŠžŃˆŠøŠ±ŠŗŠ° ŠæрŠø рŠ°Š·Š±Š¾Ń€Šµ Š¼Š°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾Š³Š¾ Š²Ń€ŠµŠ¼ŠµŠ½Šø '%s' + + + Error parsing maximum file size '%s' + ŠžŃˆŠøŠ±ŠŗŠ° ŠæрŠø рŠ°Š·Š±Š¾Ń€Šµ Š¼Š°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾Š³Š¾ рŠ°Š·Š¼ŠµŃ€Š° фŠ°Š¹Š»Š° '%s' + + + Invalid argument to '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s' + + + Invalid parameters + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Šµ ŠæŠ°Ń€Š°Š¼ŠµŃ‚ры + + + Enter password: + Š’Š²ŠµŠ“ŠøтŠµ ŠæŠ°Ń€Š¾Š»ŃŒ: + + + Failed to read new password from file + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾Ń‡ŠµŃŃ‚ŃŒ Š½Š¾Š²Ń‹Š¹ ŠæŠ°Ń€Š¾Š»ŃŒ ŠøŠ· фŠ°Š¹Š»Š° + + + Incorrect arguments to '%s' + ŠŠµŠŗŠ¾Ń€Ń€ŠµŠŗтŠ½Ń‹Šµ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚Ń‹ Šŗ '%s' + + + Invalid vm-process-priority '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ vm-process-priority '%s' + + + Invalid autostart delay number '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ Š²Ń€ŠµŠ¼Ń Š·Š°Š“ŠµŃ€Š¶ŠŗŠø Š°Š²Ń‚Š¾ŃŃ‚Š°Ń€Ń‚Š° '%s' + + + + ControlVM + + + Invalid %s number '%s'. + + + + + Failed to send a scancode. + + + + + Out of memory allocating %d bytes. + + + + + + + + + File size %RI64 is greater than %RI64: '%s'. + + + + + Cannot get size of file '%s': %Rrc. + + + + + Cannot open file '%s': %Rrc. + + + + + + Not enough parameters. + + + + + Machine '%s' is not currently running. + ŠœŠ°ŃˆŠøŠ½Š° '%s' сŠµŠ¹Ń‡Š°Ń Š½Šµ Š·Š°ŠæущŠµŠ½Š°. + + + + + + + + + + + + + + + + + + + + + + + + + + + Missing argument to '%s'. + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s'. + + + + + + Invalid value '%s'. + + + + + + Audio adapter not enabled in VM configuration. + + + + + + Missing argument to '%s %s'. + + + + + + Invalid '%s %s' argument '%s'. + + + + + + Invalid '%s' argument '%s'. + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s' '%s'. + + + + Failed to power off machine. + + + + + Machine in invalid state %d -- %s. + + + + + Failed to save machine state. + + + + + + + + + + + + + + Guest not running. + + + + + Current installed Guest Additions don't support rebooting the guest. + + + + + Current installed Guest Additions don't support shutting down the guest. + + + + + Missing argument to '%s'. Expected IBM PC AT set 2 keyboard scancode(s). + + + + + Converting '%s' returned %Rrc! + ŠŸŃ€ŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Š½ŠøŠµ '%s' Š²ŠµŃ€Š½ŃƒŠ»Š¾ %Rrc! + + + + '%s' is not a hex byte! + + + + + Missing argument to '%s'. Expected ASCII string(s). + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s'. ŠžŠ¶ŠøŠ“Š°ŠµŃ‚ся стрŠ¾ŠŗŠ° ASCII. + + + + Invalid link state '%s'. + + + + + Filename not specified for NIC %lu. + + + + + The NIC %d is currently disabled and thus its tracefile can't be changed. + + + + + Invalid nictrace%lu argument '%s'. + + + + + The NIC %d is currently disabled and thus its trace flag can't be changed. + + + + + Missing or invalid argument to '%s'. + + + + + Wrong rule proto '%s' specified -- only 'udp' and 'tcp' are allowed. + Š£ŠŗŠ°Š·Š°Š½ Š½ŠµŠæрŠ°Š²ŠøŠ»ŃŒŠ½Ń‹Š¹ ŠæрŠ¾Ń‚Š¾ŠŗŠ¾Š» ŠæрŠ°Š²ŠøŠ»Š° '%s' -- рŠ°Š·Ń€ŠµŃˆŠµŠ½Ń‹ тŠ¾Š»ŃŒŠŗŠ¾ 'udp' Šø 'tcp'. + + + + Invalid nicproperty%d argument '%s'. + + + + + Failed to allocate memory for nicproperty%d '%s'. + + + + + The NIC %d is currently disabled and thus its properties can't be changed. + + + + + Unknown promiscuous mode policy '%s'. + + + + + The NIC %d is currently disabled and thus its promiscuous mode can't be changed. + + + + + Invalid type '%s' specfied for NIC %lu. + + + + + The NIC %d is currently disabled and thus its attachment type can't be changed. + + + + + 'vrdp' is deprecated. Use 'vrde'. + + + + + Invalid remote desktop server state '%s'. + + + + + 'vrdpport' is deprecated. Use 'vrdeport'. + + + + + 'vrdpvideochannelquality' is deprecated. Use 'vrdevideochannelquality'. + + + + + Invalid vrdeproperty argument '%s'. + + + + + Failed to allocate memory for VRDE property '%s'. + + + + + Wrong number of arguments. + + + + + + + Invalid parameter '%s'. + + + + + Zero UUID argument '%s'. + + + + + + + + + + + + + + + + + + + + + Incorrect number of parameters. + + + + + Either "yes" or "no" is expected. + + + + + Display status must be <on> or <off>. + + + + + Error parsing guest memory balloon size '%s'. + + + + + Teleportation failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾ŠøŠ·Š²ŠµŃŃ‚Šø ŠæŠ¾Ń€Ń‚ŠøрŠ¾Š²Š°Š½ŠøŠµ + + + + Error parsing display number '%s'. + + + + + Failed to create file '%s' (%Rrc). + + + + + Failed to write screenshot to file '%s' (%Rrc). + + + + + Error parsing list of screen IDs '%s'. + + + + + Error parsing video width '%s'. + + + + + Error parsing video height '%s'. + + + + + Error parsing video rate '%s'. + + + + + Error parsing video FPS '%s'. + + + + + Error parsing maximum time '%s'. + + + + + Error parsing maximum file size '%s'. + + + + + Invalid argument to '%s'. + + + + + Invalid parameters. + + + + + Enter password: + Š’Š²ŠµŠ“ŠøтŠµ ŠæŠ°Ń€Š¾Š»ŃŒ: + + + + Failed to read new password from file. + + + + + + + + + + Incorrect arguments to '%s'. + + + + + Invalid vm-process-priority '%s'. + + + + + Invalid autostart delay number '%s'. + + + + + DHCPServer + + + Either --network or --interface, not both + Š˜Š»Šø --network ŠøŠ»Šø --interface, Š½Šµ Š²Š¼ŠµŃŃ‚Šµ + + + + Either --interface or --network, not both + Š˜Š»Šø --interface ŠøŠ»Šø --network, Š½Šµ Š²Š¼ŠµŃŃ‚Šµ + + + + Failed to locate host-only interface '%s' + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š¾Š±Š½Š°Ń€ŃƒŠ¶Šøть Š²ŠøртуŠ°Š»ŃŒŠ½Ń‹Š¹ ŠøŠ½Ń‚ŠµŃ€Ń„ŠµŠ¹Ń хŠ¾ŃŃ‚Š°'%s' + + + + Failed to find DHCP server for network '%s' + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š½Š°Š¹Ń‚Šø DHCP сŠµŃ€Š²ŠµŃ€ Š“Š»Ń сŠµŃ‚Šø '%s' + + + + Failed to find DHCP server for host-only interface '%s' (network '%ls') + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š½Š°Š¹Ń‚Šø DHCP сŠµŃ€Š²ŠµŃ€ Š“Š»Ń Š²ŠøртуŠ°Š»ŃŒŠ½Š¾Š³Š¾ ŠøŠ½Ń‚ŠµŃ€Ń„ŠµŠ¹ŃŠ° хŠ¾ŃŃ‚Š° '%s' (сŠµŃ‚ŃŒ '%ls') + + + + + You need to specify either --network or --interface to identify the DHCP server + ŠŃƒŠ¶Š½Š¾ уŠŗŠ°Š·Š°Ń‚ŃŒ Š»ŠøŠ±Š¾ --network Š»ŠøŠ±Š¾ --interface Š“Š»Ń ŠøŠ“ŠµŠ½Ń‚ŠøфŠøŠŗŠ°Ń†ŠøŠø DHCP сŠµŃ€Š²ŠµŃ€Š° + + + + Incomplete option sequence preseeding '--global' + ŠŠµŠæŠ¾Š»Š½Š°Ń ŠæŠ¾ŃŠ»ŠµŠ“Š¾Š²Š°Ń‚ŠµŠ»ŃŒŠ½Š¾ŃŃ‚ŃŒ Š¾ŠæцŠøŠ¹ ŠæŠµŃ€ŠµŠ“ '--global' + + + + Incomplete option sequence preseeding '--group' + ŠŠµŠæŠ¾Š»Š½Š°Ń ŠæŠ¾ŃŠ»ŠµŠ“Š¾Š²Š°Ń‚ŠµŠ»ŃŒŠ½Š¾ŃŃ‚ŃŒ Š¾ŠæцŠøŠ¹ ŠæŠµŃ€ŠµŠ“ '--group' + + + + Group name cannot be empty + Š˜Š¼Ń Š³Ń€ŃƒŠæŠæы Š½Šµ Š¼Š¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ ŠæустыŠ¼ + + + + Incomplete option sequence preseeding '--mac-address' + ŠŠµŠæŠ¾Š»Š½Š°Ń ŠæŠ¾ŃŠ»ŠµŠ“Š¾Š²Š°Ń‚ŠµŠ»ŃŒŠ½Š¾ŃŃ‚ŃŒ Š¾ŠæцŠøŠ¹ ŠæŠµŃ€ŠµŠ“ '--mac-address' + + + + Incomplete option sequence preseeding '--vm' + ŠŠµŠæŠ¾Š»Š½Š°Ń ŠæŠ¾ŃŠ»ŠµŠ“Š¾Š²Š°Ń‚ŠµŠ»ŃŒŠ½Š¾ŃŃ‚ŃŒ Š¾ŠæцŠøŠ¹ ŠæŠµŃ€ŠµŠ“ '--vm' + + + + --nic option requires a --vm preceeding selecting the VM it should apply to + ŠžŠæцŠøя --nic трŠµŠ±ŃƒŠµŃ‚ Š½Š°Š»ŠøчŠøя ŠæрŠµŠ“шŠµŃŃ‚Š²ŃƒŃŽŃ‰ŠµŠ¹ --vm Š“Š»Ń Š²Ń‹Š±Š¾Ń€Š° Š’Šœ, Šŗ ŠŗŠ¾Ń‚Š¾Ń€Š¾Š¹ Š“Š°Š½Š½Š°Ń Š¾ŠæцŠøя ŠæрŠøŠ¼ŠµŠ½ŃŠµŃ‚ся + + + + Incomplete option sequence preseeding '--nic=%u + ŠŠµŠæŠ¾Š»Š½Š°Ń ŠæŠ¾ŃŠ»ŠµŠ“Š¾Š²Š°Ń‚ŠµŠ»ŃŒŠ½Š¾ŃŃ‚ŃŒ Š¾ŠæцŠøŠ¹ ŠæŠµŃ€ŠµŠ“ '--nic=%u' + + + + invalid NIC number: %u + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š½Š¾Š¼ŠµŃ€ NIC: %u + + + + Malformed hex string given to --set-opt-hex %u: %s + + ŠŠµŠæрŠ°Š²ŠøŠ»ŃŒŠ½Š°Ń hex стрŠ¾ŠŗŠ° уŠŗŠ°Š·Š°Š½Š½Š°Ń Š² --set-opt-hex %u: %s + + + + + --del-opt does not apply to the 'add' subcommand + --del-opt Š½Šµ ŠæрŠøŠ¼ŠµŠ½ŠøŠ¼Š° Šŗ ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“Šµ 'add' + + + + --unforce-opt does not apply to the 'add' subcommand + --unforce-opt Š½Šµ ŠæрŠøŠ¼ŠµŠ½ŠøŠ¼Š° Šŗ ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“Šµ 'add' + + + + --unsuppress-opt does not apply to the 'add' subcommand + --unsuppress-opt Š½Šµ ŠæрŠøŠ¼ŠµŠ½ŠøŠ¼Š° Šŗ ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“Šµ 'add' + + + + --zap-options does not apply to the 'add' subcommand + --zap-options Š½Šµ ŠæрŠøŠ¼ŠµŠ½ŠøŠ¼Š° Šŗ ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“Šµ 'add' + + + + --remove-config does not apply to the 'add' subcommand + --remove-config Š½Šµ ŠæрŠøŠ¼ŠµŠ½ŠøŠ¼Š° Šŗ ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“Šµ 'add' + + + + --remove-config cannot be applied to the global config + --remove-config Š½Šµ Š¼Š¾Š¶ŠµŃ‚ ŠæрŠøŠ¼ŠµŠ½ŃŃ‚ŃŒŃŃ Šŗ Š³Š»Š¾Š±Š°Š»ŃŒŠ½Š¾Š¹ ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†ŠøŠø + + + + --fixed-address can only be applied to a VM NIC or an MAC address + --fixed-address ŠæрŠøŠ¼ŠµŠ½ŠøŠ¼Š¾ тŠ¾Š»ŃŒŠŗŠ¾ Šŗ NIC Š’Šœ ŠøŠ»Šø MAC Š°Š“рŠµŃŃƒ + + + + A group must be selected to perform condition alterations. + Š”Š»Ń ŠæрŠ¾Š²ŠµŠ“ŠµŠ½Šøя ŠøŠ·Š¼ŠµŠ½ŠµŠ½ŠøŠ¹ ŠæŠ¾ усŠ»Š¾Š²Šøю Š“Š¾Š»Š¶Š½Š° Š±Ń‹Ń‚ŃŒ Š²Ń‹Š±Ń€Š°Š½Š° Š³Ń€ŃƒŠæŠæŠ°. + + + + Condition value cannot be empty + Š—Š½Š°Ń‡ŠµŠ½ŠøŠµ усŠ»Š¾Š²Šøя Š½Šµ Š¼Š¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ ŠæустыŠ¼ + + + + Could not find any condition of type %d with value '%s' to delete + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š½Š°Š¹Ń‚Šø ŠŗŠ°ŠŗŠ¾Šµ-Š»ŠøŠ±Š¾ усŠ»Š¾Š²ŠøŠµ тŠøŠæŠ° %d сŠ¾ Š·Š½Š°Ń‡ŠµŠ½ŠøŠµŠ¼ '%s' Š“Š»Ń уŠ“Š°Š»ŠµŠ½Šøя + + + + --zap-conditions can only be with a group selected + --zap-conditions Š¼Š¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ тŠ¾Š»ŃŒŠŗŠ¾ с Š²Ń‹Š±Ń€Š°Š½Š½Š¾Š¹ Š³Ń€ŃƒŠæŠæŠ¾Š¹ + + + + Incomplete option sequence preseeding '--id=%u + ŠŠµŠæŠ¾Š»Š½Š°Ń ŠæŠ¾ŃŠ»ŠµŠ“Š¾Š²Š°Ń‚ŠµŠ»ŃŒŠ½Š¾ŃŃ‚ŃŒ Š¾ŠæцŠøŠ¹ ŠæŠµŃ€ŠµŠ“ '--id=%u' + + + + --value without --id=dhcp-opt-no + --value Š±ŠµŠ· --id=dhcp-opt-no + + + + --remove does not apply to the 'add' subcommand + --remove Š½Šµ ŠæрŠøŠ¼ŠµŠ½ŠøŠ¼Š° Šŗ ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“Šµ 'add' + + + + --remove without --id=dhcp-opt-no + --remove Š±ŠµŠ· --id=dhcp-opt-no + + + + Missing required option: --ip + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ трŠµŠ±ŃƒŠµŠ¼Š°Ń Š¾ŠæцŠøя: --ip + + + + Missing required option: --netmask + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ трŠµŠ±ŃƒŠµŠ¼Š°Ń Š¾ŠæцŠøя: --netmask + + + + Missing required option: --lowerip + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ трŠµŠ±ŃƒŠµŠ¼Š°Ń Š¾ŠæцŠøя: --lowerip + + + + Missing required option: --upperip + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ трŠµŠ±ŃƒŠµŠ¼Š°Ń Š¾ŠæцŠøя: --upperip + + + + Could not find interface '%s' + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š½Š°Š¹Ń‚Šø ŠøŠ½Ń‚ŠµŃ€Ń„ŠµŠ¹Ń '%s' + + + + Could not get network name for the interface '%s' + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæŠ¾Š»ŃƒŃ‡Šøть ŠøŠ¼Ń сŠµŃ‚Šø Š“Š»Ń ŠøŠ½Ń‚ŠµŃ€Ń„ŠµŠ¹ŃŠ° '%s' + + + + DHCP server already exists + DHCP сŠµŃ€Š²ŠµŃ€ уŠ¶Šµ сущŠµŃŃ‚Š²ŃƒŠµŃ‚ + + + + Failed to create the DHCP server + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ сŠ¾Š·Š“Š°Ń‚ŃŒ DHCP сŠµŃ€Š²ŠµŃ€ + + + + DHCP server does not exist + DHCP сŠµŃ€Š²ŠµŃ€ Š½Šµ сущŠµŃŃ‚Š²ŃƒŠµŃ‚ + + + + Failed to set configuration (%ls, %ls, %ls, %ls) + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°Š“Š°Ń‚ŃŒ ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†Šøю (%ls, %ls, %ls, %ls) + + + + Failed to remove server + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ уŠ“Š°Š»Šøть сŠµŃ€Š²ŠµŃ€ + + + + Failed to start the server + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°ŠæустŠøть сŠµŃ€Š²ŠµŃ€ + + + + Failed to restart the server + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠµŃ€ŠµŠ·Š°ŠæустŠøть сŠµŃ€Š²ŠµŃ€ + + + + Failed to stop the server + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š¾ŃŃ‚Š°Š½Š¾Š²Šøть сŠµŃ€Š²ŠµŃ€ + + + + You need to specify a MAC address too look for + ŠŠµŠ¾Š±Ń…Š¾Š“ŠøŠ¼Š¾ уŠŗŠ°Š·Š°Ń‚ŃŒ MAC Š°Š“рŠµŃ Š“Š»Ń ŠæŠ¾ŠøсŠŗŠ° + + + + IP Address: %ls +MAC Address: %RTmac +State: %ls +Issued: %s (%RU64) +Expire: %s (%RU64) +TTL: %RU64 sec, currently %RU64 sec left + + IP Š°Š“рŠµŃ: %ls +MAC Š°Š“рŠµŃ: %RTmac +Š”Š¾ŃŃ‚Š¾ŃŠ½ŠøŠµ: %ls +Š’Ń‹Š“Š°Š½: %s (%RU64) +Š˜ŃŃ‚ŠµŠŗŠ°ŠµŃ‚: %s (%RU64) +TTL: %RU64 сŠµŠŗ, сŠµŠ¹Ń‡Š°Ń %RU64 сŠµŠŗ Š¾ŃŃ‚Š°Š»Š¾ŃŃŒ + + + + + DebugVM + + + The getregisters sub-command takes at least one register name + ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“Š° getregisters ŠæрŠøŠ½ŠøŠ¼Š°ŠµŃ‚ ŠøŠ¼Ń ŠŗŠ°Šŗ Š¼ŠøŠ½ŠøŠ¼ŃƒŠ¼ Š¾Š“Š½Š¾Š³Š¾ рŠµŠ³ŠøстрŠ° + + + + Must specify info item to display + ŠŠµŠ¾Š±Ń…Š¾Š“ŠøŠ¼Š¾ уŠŗŠ°Š·Š°Ń‚ŃŒ эŠ»ŠµŠ¼ŠµŠ½Ń‚ Š“Š»Ń Š¾Ń‚Š¾Š±Ń€Š°Š¶ŠµŠ½Šøя + + + + The --compression option has already been given + ŠžŠæцŠøя --compression уŠ¶Šµ ŠæрŠµŠ“Š¾ŃŃ‚Š°Š²Š»ŠµŠ½Š° + + + + The --filename option has already been given + ŠžŠæцŠøя --filename уŠ¶Šµ ŠæрŠµŠ“Š¾ŃŃ‚Š°Š²Š»ŠµŠ½Š° + + + + The --filename option is required + Š¢Ń€ŠµŠ±ŃƒŠµŃ‚ся Š¾ŠæцŠøя --filename + + + + RTPathAbs failed on '%s': %Rrc + RTPathAbs Š·Š°Š²ŠµŃ€ŃˆŠøŠ»ŃŃ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹ Š½Š° '%s': %Rrc + + + + Detected: %ls + + ŠžŠ±Š½Š°Ń€ŃƒŠ¶ŠµŠ½Š¾: %ls + + + + + Name: %ls + + Š˜Š¼Ń: %ls + + + + + Version: %ls + + Š’ŠµŃ€ŃŠøя: %ls + + + + + setregisters expects input on the form 'register=value' got '%s' + setregisters Š¾Š¶ŠøŠ“Š°ŠµŃ‚ Š²Ń…Š¾Š“Š½Ń‹Ń… Š“Š°Š½Š½Ń‹Ń… Š² фŠ¾Ń€Š¼Š°Ń‚Šµ 'рŠµŠ³Šøстр=Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ', ŠæŠ¾Š»ŃƒŃ‡ŠµŠ½Š¾ '%s' + + + + Out of memory + + ŠŠµ хŠ²Š°Ń‚Š°ŠµŃ‚ ŠæŠ°Š¼ŃŃ‚Šø + + + + + The setregisters sub-command takes at least one register name + ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“Š° setregisters ŠæрŠøŠ½ŠøŠ¼Š°ŠµŃ‚ ŠøŠ¼Ń ŠŗŠ°Šŗ Š¼ŠøŠ½ŠøŠ¼ŃƒŠ¼ Š¾Š“Š½Š¾Š³Š¾ рŠµŠ³ŠøстрŠ° + + + + Successfully set %ls + + Š£ŃŠæŠµŃˆŠ½Š¾ устŠ°Š½Š¾Š²Š»ŠµŠ½ %ls + + + + + Successfully set %u registers + + + Š£ŃŠæŠµŃˆŠ½Š¾ устŠ°Š½Š¾Š²Š»ŠµŠ½Š¾ %u рŠµŠ³Šøстр + + Š£ŃŠæŠµŃˆŠ½Š¾ устŠ°Š½Š¾Š²Š»ŠµŠ½Š¾ %u рŠµŠ³ŠøстрŠ° + + Š£ŃŠæŠµŃˆŠ½Š¾ устŠ°Š½Š¾Š²Š»ŠµŠ½Š¾ %u рŠµŠ³ŠøстрŠ¾Š² + + + + + + export %s='%ls' + + export %s='%ls' + + + + + set %s=%ls + + set %s=%ls + + + + + Debug logger settings: + + ŠŠ°ŃŃ‚Ń€Š¾Š¹ŠŗŠø Š¶ŃƒŃ€Š½Š°Š»ŠøрŠ¾Š²Š°Š½Šøя (Š¾Ń‚Š»Š°Š“ŠŗŠ°): + + + + + Release logger settings: + + ŠŠ°ŃŃ‚Ń€Š¾Š¹ŠŗŠø Š¶ŃƒŃ€Š½Š°Š»ŠøрŠ¾Š²Š°Š½Šøя (рŠµŠ»ŠøŠ·): + + + + + The show sub-command has no idea what '%s' might be + Š£ ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“ы show Š½ŠµŃ‚ ŠøŠ“ŠµŠ¹ чтŠ¾ этŠ¾ Š¼Š¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ - '%s' + + + + ====================== CPU #%u ====================== + + ====================== Š¦ŠŸŠ£ #%u ====================== + + + + + Multiple --pattern options are not permitted + ŠœŠ½Š¾Š¶ŠµŃŃ‚Š²ŠµŠ½Š½Ń‹Šµ Š¾ŠæцŠøŠø --pattern Š½Šµ рŠ°Š·Ń€ŠµŃˆŠ°ŃŽŃ‚ся + + + + The --reset and --descriptions options does not mix + ŠžŠæцŠøŠø --reset Šø --descriptions Š½Šµ сŠ¼ŠµŃˆŠøŠ²Š°ŃŽŃ‚ся + + + + The --filename is missing + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ --filename + + + + Machine '%s' is not currently running. + + ŠœŠ°ŃˆŠøŠ½Š° '%s' сŠµŠ¹Ń‡Š°Ń Š½Šµ Š·Š°ŠæущŠµŠ½Š°. + + + + + Disk + + + Error code %Rrc at %s(%u) in function %s + ŠšŠ¾Š“ Š¾ŃˆŠøŠ±ŠŗŠø %Rrc Š² %s(%u) Š² фуŠ½ŠŗцŠøŠø %s + + + + + Cannot convert filename "%s" to absolute path + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæрŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Ń‚ŃŒ ŠøŠ¼Ń фŠ°Š¹Š»Š° "%s" Š² Š°Š±ŃŠ¾Š»ŃŽŃ‚Š½Ń‹Š¹ Šæуть + + + + Out of memory copying '%s' + ŠŠµ хŠ²Š°Ń‚Š°ŠµŃ‚ ŠæŠ°Š¼ŃŃ‚Šø Š“Š»Ń ŠŗŠ¾ŠæŠøрŠ¾Š²Š°Š½Šøя '%s' + + + + + + + + + + + + + + + + + + Only one command can be specified: '%s' + ŠœŠ¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ уŠŗŠ°Š·Š°Š½Š° тŠ¾Š»ŃŒŠŗŠ¾ Š¾Š“Š½Š° ŠŗŠ¾Š¼Š°Š½Š“Š°: '%s' + + + + Invalid key value pair: No '='. + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š°Ń ŠæŠ°Ń€Š° ŠŗŠ»ŃŽŃ‡/Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ: ŠŠµŃ‚ '='. + + + + Cannot open replacement value file '%s': %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š¾Ń‚Šŗрыть фŠ°Š¹Š» с ŠæŠ¾Š“Š¼ŠµŠ½ŃŠµŠ¼Ń‹Š¼ Š·Š½Š°Ń‡ŠµŠ½ŠøŠµŠ¼ '%s': %Rrc + + + + Error reading replacement MBR file '%s': %Rrc + ŠžŃˆŠøŠ±ŠŗŠ° чтŠµŠ½Šøя фŠ°Š¹Š»Š° с ŠæŠ¾Š“Š¼ŠµŠ½ŃŠµŠ¼Ń‹Š¼ MBR '%s': %Rrc + + + + Out of memory reading '%s': %Rrc + ŠŠµ хŠ²Š°Ń‚Š°ŠµŃ‚ ŠæŠ°Š¼ŃŃ‚Šø ŠæрŠø чтŠµŠ½ŠøŠø '%s': %Rrc + + + + Replacement value file '%s' is to big: %Rhcb, max 16MiB + Š¤Š°Š¹Š» с ŠæŠ¾Š“Š¼ŠµŠ½ŃŠµŠ¼Ń‹Š¼ Š·Š½Š°Ń‡ŠµŠ½ŠøŠµŠ¼ '%s' сŠ»ŠøшŠŗŠ¾Š¼ Š±Š¾Š»ŃŒŃˆŠ¾Š¹: %Rhcb, Š¼Š°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ 16MiB + + + + Cannot get the size of the value file '%s': %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæŠ¾Š»ŃƒŃ‡Šøть рŠ°Š·Š¼ŠµŃ€ фŠ°Š¹Š»Š° '%s', сŠ¾Š“ŠµŃ€Š¶Š°Ń‰ŠøŠ¼ Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ: %Rrc + + + + + + + Invalid medium variant '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š²Š°Ń€ŠøŠ°Š½Ń‚ Š½Š¾ŃŠøтŠµŠ»Ń '%s' + + + + + + + + + + Invalid parameter '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ '%s' + + + + + + + + Invalid option -%c + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š°Ń Š¾ŠæцŠøя -%c + + + + + + + + Invalid option case %i + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š²Š°Ń€ŠøŠ°Š½Ń‚ Š¾ŠæцŠøŠø %i + + + + + + + + unknown option: %s + + Š½ŠµŠøŠ·Š²ŠµŃŃ‚Š½Š°Ń Š¾ŠæцŠøя: %s + + + + + + + + + + error: %Rrs + Š¾ŃˆŠøŠ±ŠŗŠ°: %Rrs + + + + + Parameters --filename is required + Š¢Ń€ŠµŠ±ŃƒŠµŃ‚ся ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ --filename + + + + Parameters --size is required + Š¢Ń€ŠµŠ±ŃƒŠµŃ‚ся ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ --size + + + + Creating a differencing medium is only supported for hard disks + Š”Š¾Š·Š“Š°Š½ŠøŠµ рŠ°Š·Š½Š¾ŃŃ‚Š½Š¾Š³Š¾ Š½Š¾ŃŠøтŠµŠ»Ń ŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŃ‚ся тŠ¾Š»ŃŒŠŗŠ¾ Š“Š»Ń Š¶ŠµŃŃ‚ŠŗŠøх Š“ŠøсŠŗŠ¾Š² + + + + Invalid parent hard disk reference, avoiding crash + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š°Ń ссыŠ»ŠŗŠ° Š½Š° рŠ¾Š“ŠøтŠµŠ»ŃŒŃŠŗŠøŠ¹ Š¶ŠµŃŃ‚ŠŗŠøŠ¹ Š“ŠøсŠŗ, ŠæрŠµŠ“Š¾Ń‚Š²Ń€Š°Ń‰ŠµŠ½ŠøŠµ ŠŗрŠ°Ń…Š° + + + + The %s is not found in the property list of the requested medium format. + %s Š½Šµ Š½Š°Š¹Š“ŠµŠ½ Š² сŠæŠøсŠŗŠµ сŠ²Š¾Š¹ŃŃ‚Š² Š·Š°ŠæрŠ¾ŃˆŠµŠ½Š½Š¾Š³Š¾ фŠ¾Ń€Š¼Š°Ń‚Š° Š½Š¾ŃŠøтŠµŠ»Ń. + + + + Base64 encoding of the property %s failed. (%Rhrc) + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°ŠŗŠ¾Š“ŠøрŠ¾Š²Š°Ń‚ŃŒ сŠ²Š¾Š¹ŃŃ‚Š²Š¾, ŠøсŠæŠ¾Š»ŃŒŠ·ŃƒŃ Base64. (%Rhrc) + + + + Failed to create medium + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Š·Š“Š°Ń‚ŃŒ Š½Š¾ŃŠøтŠµŠ»ŃŒ + + + + Medium created. UUID: %s + + ŠŠ¾ŃŠøтŠµŠ»ŃŒ сŠ¾Š·Š“Š°Š½. UUID: %s + + + + + Invalid medium type '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ тŠøŠæ Š½Š¾ŃŠøтŠµŠ»Ń '%s' + + + + Invalid autoreset parameter '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ Š°Š²Ń‚Š¾ŃŠ±Ń€Š¾ŃŠ° '%s' + + + + Invalid --property argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --property '%s' + + + + Error: Failed to allocate memory for medium property '%s' + + ŠžŃˆŠøŠ±ŠŗŠ°: ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š²Ń‹Š“ŠµŠ»Šøть ŠæŠ°Š¼ŃŃ‚ŃŒ Š“Š»Ń сŠ²Š¾Š¹ŃŃ‚Š²Š° Š½Š¾ŃŠøтŠµŠ»Ń '%s' + + + + + + + Medium name or UUID required + Š¢Ń€ŠµŠ±ŃƒŠµŃ‚ся ŠøŠ¼Ń ŠøŠ»Šø UUID Š½Š¾ŃŠøтŠµŠ»Ń + + + + No operation specified + ŠŠµ уŠŗŠ°Š·Š°Š½Š° Š¾ŠæŠµŃ€Š°Ń†Šøя + + + + Invalid medium reference, avoiding crash + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š°Ń ссыŠ»ŠŗŠ° Š½Š° Š½Š¾ŃŠøтŠµŠ»ŃŒ, ŠæрŠµŠ“Š¾Ń‚Š²Ń€Š°Ń‰ŠµŠ½ŠøŠµ ŠŗрŠ°Ń…Š° + + + + Error: Attempt to resize the medium from %RU64.%RU64 MB to %RU64.%RU64 MB. Use --resizebyte if this is intended! + + ŠžŃˆŠøŠ±ŠŗŠ°: ŠŸŠ¾ŠæытŠŗŠ° ŠøŠ·Š¼ŠµŠ½Šøть рŠ°Š·Š¼ŠµŃ€ Š½Š¾ŃŠøтŠµŠ»Ń с %RU64.%RU64 MB Š² %RU64.%RU64 MB. Š˜ŃŠæŠ¾Š»ŃŒŠ·ŃƒŠ¹Ń‚Šµ --resizebyte, ŠµŃŠ»Šø этŠ¾ тŠ°Šŗ Šø Š“Š¾Š»Š¶Š½Š¾ Š±Ń‹Ń‚ŃŒ! + + + + + Compact medium operation is not implemented! + ŠžŠæŠµŃ€Š°Ń†Šøя сŠ¶Š°Ń‚Šøя Š½Š¾ŃŠøтŠµŠ»Ń Š½Šµ рŠµŠ°Š»ŠøŠ·Š¾Š²Š°Š½Š°! + + + + Compact medium operation for this format is not implemented yet! + ŠžŠæŠµŃ€Š°Ń†Šøя сŠ¶Š°Ń‚Šøя Š½Š¾ŃŠøтŠµŠ»Ń Š“Š»Ń этŠ¾Š³Š¾ фŠ¾Ń€Š¼Š°Ń‚Š° ŠµŃ‰Šµ Š½Šµ рŠµŠ°Š»ŠøŠ·Š¾Š²Š°Š½Š°! + + + + Failed to compact medium + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¶Š°Ń‚ŃŒ Š½Š¾ŃŠøтŠµŠ»ŃŒ + + + + Failed to compact medium! + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¶Š°Ń‚ŃŒ Š½Š¾ŃŠøтŠµŠ»ŃŒ! + + + + Failed to resize medium + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠøŠ·Š¼ŠµŠ½Šøть рŠ°Š·Š¼ŠµŃ€ Š½Š¾ŃŠøтŠµŠ»Ń + + + + Resize medium operation is not implemented! + ŠžŠæŠµŃ€Š°Ń†Šøя ŠøŠ·Š¼ŠµŠ½ŠµŠ½Šøя рŠ°Š·Š¼ŠµŃ€Š° Š½Š¾ŃŠøтŠµŠ»Ń Š½Šµ рŠµŠ°Š»ŠøŠ·Š¾Š²Š°Š½Š°! + + + + Resize medium operation for this format is not implemented yet! + ŠžŠæŠµŃ€Š°Ń†Šøя ŠøŠ·Š¼ŠµŠ½ŠµŠ½Šøя рŠ°Š·Š¼ŠµŃ€Š° Š½Š¾ŃŠøтŠµŠ»Ń Š“Š»Ń этŠ¾Š³Š¾ фŠ¾Ń€Š¼Š°Ń‚Š° ŠµŃ‰Šµ Š½Šµ рŠµŠ°Š»ŠøŠ·Š¾Š²Š°Š½Š°! + + + + Failed to resize medium! + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠøŠ·Š¼ŠµŠ½Šøть рŠ°Š·Š¼ŠµŃ€ Š½Š¾ŃŠøтŠµŠ»Ń! + + + + Failed to move medium + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠµŃ€ŠµŠ¼ŠµŃŃ‚Šøть Š½Š¾ŃŠøтŠµŠ»ŃŒ + + + + Move medium with UUID %s finished + + ŠŸŠµŃ€ŠµŠ¼ŠµŃ‰ŠµŠ½ŠøŠµ Š½Š¾ŃŠøтŠµŠ»Ń с UUID %s Š·Š°ŠŗŠ¾Š½Ń‡ŠµŠ½Š¾ + + + + + Set new location of medium with UUID %s finished + + Š£ŃŃ‚Š°Š½Š¾Š²ŠŗŠ° Š½Š¾Š²Š¾Š³Š¾ Š¼ŠµŃŃ‚Š¾ŠæŠ¾Š»Š¾Š¶ŠµŠ½Šøя Š½Š¾ŃŠøтŠµŠ»Ń с UUID %s Š·Š°ŠŗŠ¾Š½Ń‡ŠµŠ½Š° + + + + + Medium description has been changed. + + ŠžŠæŠøсŠ°Š½ŠøŠµ Š½Š¾ŃŠøтŠµŠ»Ń ŠøŠ·Š¼ŠµŠ½ŠµŠ½Š¾. + + + + + unhandled option: -%c + Š½ŠµŠ¾Š±Ń€Š°Š±Š¾Ń‚Š°Š½Š½Š°Ń Š¾ŠæцŠøя: -%c + + + + unhandled option: %i + Š½ŠµŠ¾Š±Ń€Š°Š±Š¾Ń‚Š°Š½Š½Š°Ń Š¾ŠæцŠøя: %i + + + + unknown option: %s + Š½ŠµŠøŠ·Š²ŠµŃŃ‚Š½Š°Ń Š¾ŠæцŠøя: %s + + + + Mandatory UUID or input file parameter missing + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š¾Š±ŃŠ·Š°Ń‚ŠµŠ»ŃŒŠ½Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€: ŠøŠ»Šø UUID ŠøŠ»Šø Š²Ń…Š¾Š“Š½Š¾Š¹ фŠ°Š¹Š» + + + + Mandatory output file parameter missing + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š¾Š±ŃŠ·Š°Ń‚ŠµŠ»ŃŒŠ½Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€: Š²Ń‹Ń…Š¾Š“Š½Š¾Š¹ фŠ°Š¹Š» + + + + Specified options which cannot be used with --existing + Š£ŠŗŠ°Š·Š°Š½Š° Š¾ŠæцŠøя, ŠŗŠ¾Ń‚Š¾Ń€Š°Ń Š½Šµ Š¼Š¾Š¶ŠµŃ‚ ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŃŒŃŃ с --existing + + + + Failed to clone medium + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠŗŠ»Š¾Š½ŠøрŠ¾Š²Š°Ń‚ŃŒ Š½Š¾ŃŠøтŠµŠ»ŃŒ + + + + Clone medium created in format '%ls'. UUID: %s + + Š”Š¾Š·Š“Š°Š½ ŠŗŠ»Š¾Š½ŠøрŠ¾Š²Š°Š½Š½Ń‹Š¹ Š½Š¾ŃŠøтŠµŠ»ŃŒ Š² фŠ¾Ń€Š¼Š°Ń‚Šµ '%ls'. UUID: %s + + + + + Invalid UUID '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ UUID '%s' + + + + Incorrect number of parameters + ŠŠµŠŗŠ¾Ń€Ń€ŠµŠŗтŠ½Š¾Šµ ŠŗŠ¾Š»ŠøчŠµŃŃ‚Š²Š¾ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š¾Š² + + + + Converting from raw image file="%s" to file="%s"... + + ŠŸŃ€ŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Š½ŠøŠµ ŠøŠ· фŠ°Š¹Š»Š° raw Š¾Š±Ń€Š°Š·Š° ="%s" Š² фŠ°Š¹Š» ="%s"... + + + + + Cannot open file "%s": %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š¾Ń‚Šŗрыть фŠ°Š¹Š» "%s": %Rrc + + + + Cannot get image size for file "%s": %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæŠ¾Š»ŃƒŃ‡Šøть рŠ°Š·Š¼ŠµŃ€ Š¾Š±Ń€Š°Š·Š° Š“Š»Ń фŠ°Š¹Š»Š° "%s": %Rrc + + + + Creating %s image with size %RU64 bytes (%RU64MB)... + + + Š”Š¾Š·Š“Š°Š½ŠøŠµ %s Š¾Š±Ń€Š°Š·Š° рŠ°Š·Š¼ŠµŃ€Š¾Š¼ %RU64 Š±Š°Š¹Ń‚ (%RU64MB)... + + Š”Š¾Š·Š“Š°Š½ŠøŠµ %s Š¾Š±Ń€Š°Š·Š° рŠ°Š·Š¼ŠµŃ€Š¾Š¼ %RU64 Š±Š°Š¹Ń‚Š° (%RU64MB)... + + Š”Š¾Š·Š“Š°Š½ŠøŠµ %s Š¾Š±Ń€Š°Š·Š° рŠ°Š·Š¼ŠµŃ€Š¾Š¼ %RU64 Š±Š°Š¹Ń‚ (%RU64MB)... + + + + + + fixed + фŠøŠŗсŠøрŠ¾Š²Š°Š½Š½Ń‹Š¹ + + + + dynamic + Š“ŠøŠ½Š°Š¼ŠøчŠµŃŠŗŠøŠ¹ + + + + fixed + adjective + фŠøŠŗсŠøрŠ¾Š²Š°Š½Š½Š¾Š³Š¾ + + + + dynamic + adjective + Š“ŠøŠ½Š°Š¼ŠøчŠµŃŠŗŠ¾Š³Š¾ + + + + Converted image from %s + ŠŸŃ€ŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Š½ Š¾Š±Ń€Š°Š· ŠøŠ· %s + + + + Cannot create the virtual disk container: %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ сŠ¾Š·Š“Š°Ń‚ŃŒ ŠŗŠ¾Š½Ń‚ŠµŠ¹Š½ŠµŃ€ Š²ŠøртуŠ°Š»ŃŒŠ½Š¾Š³Š¾ Š“ŠøсŠŗŠ°: %Rrc + + + + Cannot create the disk image "%s": %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ сŠ¾Š·Š“Š°Ń‚ŃŒ Š¾Š±Ń€Š°Š· Š“ŠøсŠŗŠ° "%s": %Rrc + + + + Out of memory allocating buffers for image "%s": %Rrc + ŠŠµ хŠ²Š°Ń‚Š°ŠµŃ‚ ŠæŠ°Š¼ŃŃ‚Šø ŠæŠ¾Š“ Š±ŃƒŃ„ŠµŃ€Š¾Š² Š“Š»Ń Š¾Š±Ń€Š°Š·Š° "%s": %Rrc + + + + Failed to write to disk image "%s": %Rrc + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°ŠæŠøсŠ°Ń‚ŃŒ Š² Š¾Š±Ń€Š°Š· Š“ŠøсŠŗŠ° "%s": %Rrc + + + + Parent UUID: %s + + Š Š¾Š“ŠøтŠµŠ»ŃŒŃŠŗŠøŠ¹ UUID: %s + + + + + + + unknown + Š½ŠµŠøŠ·Š²ŠµŃŃ‚Š½Š¾ + + + + not created + Š½Šµ сŠ¾Š·Š“Š°Š½ + + + + created + сŠ¾Š·Š“Š°Š½ + + + + locked read + Š·Š°Š±Š»Š¾ŠŗŠøрŠ¾Š²Š°Š½ Š½Š° чтŠµŠ½ŠøŠµ + + + + locked write + Š·Š°Š±Š»Š¾ŠŗŠøрŠ¾Š²Š°Š½ Š½Š° Š·Š°ŠæŠøсь + + + + inaccessible + Š½ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠµŠ½ + + + + creating + сŠ¾Š·Š“Š°Š½ŠøŠµ + + + + deleting + уŠ“Š°Š»ŠµŠ½ŠøŠµ + + + + State: %s + + Š”Š¾ŃŃ‚Š¾ŃŠ½ŠøŠµ: %s + + + + + Access Error: %ls + + ŠžŃˆŠøŠ±ŠŗŠ° Š“Š¾ŃŃ‚ŃƒŠæŠ°: %ls + + + + + Description: %ls + + ŠžŠæŠøсŠ°Š½ŠøŠµ: %ls + + + + + normal (differencing) + Š½Š¾Ń€Š¼Š°Š»ŃŒŠ½Ń‹Š¹ (рŠ°Š·Š½Š¾ŃŃ‚Š½Ń‹Š¹) + + + + normal (base) + Š½Š¾Ń€Š¼Š°Š»ŃŒŠ½Ń‹Š¹ (Š±Š°Š·Š¾Š²Ń‹Š¹) + + + + immutable + Š½ŠµŠøŠ·Š¼ŠµŠ½ŃŠµŠ¼Ń‹Š¹ + + + + writethrough + сŠŗŠ²Š¾Š·Š½Š°Ń Š·Š°ŠæŠøсь + + + + shareable + рŠ°Š·Š“ŠµŠ»ŃŠµŠ¼Ń‹Š¹ + + + + readonly + тŠ¾Š»ŃŒŠŗŠ¾ Š“Š»Ń чтŠµŠ½Šøя + + + + multiattach + Š¼Š½Š¾Š¶ŠµŃŃ‚Š²ŠµŠ½Š½Š¾Šµ ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ŠøŠµ + + + + Type: %s + + Š¢ŠøŠæ: %s + + + + + Auto-Reset: %s + + ŠŠ²Ń‚Š¾ŃŠ±Ń€Š¾Ń: %s + + + + + on + Š²ŠŗŠ» + + + + off + Š²Ń‹ŠŗŠ» + + + + Location: %ls + + Š Š°ŃŠæŠ¾Š»Š¾Š¶ŠµŠ½ŠøŠµ: %ls + + + + + Storage format: %ls + + Š¤Š¾Ń€Š¼Š°Ń‚ хрŠ°Š½ŠµŠ½Šøя: %ls + + + + + split2G + Š”ŠµŠ»Šøть ŠæŠ¾ 2G + + + + streamOptimized + Š¾ŠæтŠøŠ¼ŠøŠ·ŠøрŠ¾Š²Š°Š½ Š“Š»Ń ŠæŠ¾Ń‚Š¾ŠŗŠ° + + + + ESX + ESX + + + + default + ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю + + + + differencing + рŠ°Š·Š½Š¾ŃŃ‚Š½Ń‹Š¹ + + + + Format variant: %s %s + + Š’Š°Ń€ŠøŠ°Š½Ń‚ фŠ¾Ń€Š¼Š°Ń‚Š°: %s %s + + + + + Capacity: %lld MBytes + + Š•Š¼ŠŗŠ¾ŃŃ‚ŃŒ: %lld MB + + + + + Size on disk: %lld MBytes + + Š Š°Š·Š¼ŠµŃ€ Š½Š° Š“ŠøсŠŗŠµ: %lld MB + + + + + Encryption: enabled + + ŠØŠøфрŠ¾Š²Š°Š½ŠøŠµ: Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾ + + + + + Cipher: %ls + + ŠØŠøфр: %ls + + + + + Password ID: %ls + + ID ŠæŠ°Ń€Š¾Š»Ń: %ls + + + + + Encryption: disabled + + ŠØŠøфрŠ¾Š²Š°Š½ŠøŠµ: Š²Ń‹ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾ + + + + + Property: + Š”Š²Š¾Š¹ŃŃ‚Š²Š¾: + + + + In use by VMs: + Š˜ŃŠæŠ¾Š»ŃŒŠ·ŃƒŠµŃ‚ся Š² Š’Šœ: + + + + Child UUIDs: + Š”Š¾Ń‡ŠµŃ€Š½ŠøŠµ UUID: + + + + base + Š±Š°Š·Š¾Š²Ń‹Š¹ + + + + Failed to delete medium + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ уŠ“Š°Š»Šøть Š½Š¾ŃŠøтŠµŠ»ŃŒ + + + + Failed to delete medium. Error code %Rrc + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ уŠ“Š°Š»Šøть Š½Š¾ŃŠøтŠµŠ»ŃŒ. ŠšŠ¾Š“ Š¾ŃˆŠøŠ±ŠŗŠø %Rrc + + + + unexpected parameter %s + + Š½ŠµŠ¾Š¶ŠøŠ“Š°Š½Š½Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ %s + + + + + Missing action + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š“ŠµŠ¹ŃŃ‚Š²ŠøŠµ + + + + Invalid action given: %s + Š£ŠŗŠ°Š·Š°Š½Š¾ Š½ŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ Š“ŠµŠ¹ŃŃ‚Š²ŠøŠµ: %s + + + + Invalid number of arguments given for action: %s + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ чŠøсŠ»Š¾ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚Š¾Š², уŠŗŠ°Š·Š°Š½Š½Š¾Šµ Š“Š»Ń Š“ŠµŠ¹ŃŃ‚Š²Šøя: %s + + + + Disk name or UUID required + Š¢Ń€ŠµŠ±ŃƒŠµŃ‚ся ŠøŠ¼Ń ŠøŠ»Šø UUID Š“ŠøсŠŗŠ° + + + + No password specified + ŠŠµ уŠŗŠ°Š·Š°Š½ ŠæŠ°Ń€Š¾Š»ŃŒ + + + + A new password must always have a valid identifier set at the same time + ŠŠ¾Š²Ń‹Š¹ ŠæŠ°Ń€Š¾Š»ŃŒ Š“Š¾Š»Š¶ŠµŠ½ Š²ŃŠµŠ³Š“Š° ŠøŠ¼ŠµŃ‚ŃŒ Š“ŠµŠ¹ŃŃ‚Š²ŠøтŠµŠ»ŃŒŠ½Ń‹Š¹ ŠøŠ“ŠµŠ½Ń‚ŠøфŠøŠŗŠ°Ń‚Š¾Ń€ Š·Š°Š“Š°Š½Š½Ń‹Š¹ Š² тŠ¾ Š¶Šµ Š²Ń€ŠµŠ¼Ń + + + + Enter new password: + Š’Š²ŠµŠ“ŠøтŠµ Š½Š¾Š²Ń‹Š¹ ŠæŠ°Ń€Š¾Š»ŃŒ: + + + + Failed to read new password from file + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾Ń‡ŠµŃŃ‚ŃŒ Š½Š¾Š²Ń‹Š¹ ŠæŠ°Ń€Š¾Š»ŃŒ ŠøŠ· фŠ°Š¹Š»Š° + + + + Enter old password: + Š’Š²ŠµŠ“ŠøтŠµ стŠ°Ń€Ń‹Š¹ ŠæŠ°Ń€Š¾Š»ŃŒ: + + + + Failed to read old password from file + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾Ń‡ŠµŃŃ‚ŃŒ стŠ°Ń€Ń‹Š¹ ŠæŠ°Ń€Š¾Š»ŃŒ ŠøŠ· фŠ°Š¹Š»Š° + + + + + Invalid hard disk reference, avoiding crash + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š°Ń ссыŠ»ŠŗŠ° Š½Š° Š¶ŠµŃŃ‚ŠŗŠøŠ¹ Š“ŠøсŠŗ, ŠæрŠµŠ“Š¾Ń‚Š²Ń€Š°Ń‰ŠµŠ½ŠøŠµ ŠŗрŠ°Ń…Š° + + + + Encrypt hard disk operation is not implemented! + ŠžŠæŠµŃ€Š°Ń†Šøя шŠøфрŠ¾Š²Š°Š½Šøя Š¶ŠµŃŃ‚ŠŗŠ¾Š³Š¾ Š“ŠøсŠŗŠ° Š½Šµ рŠµŠ°Š»ŠøŠ·Š¾Š²Š°Š½Š°! + + + + Encrypt hard disk operation for this cipher is not implemented yet! + ŠžŠæŠµŃ€Š°Ń†Šøя шфŠøрŠ¾Š²Š°Š½Šøя Š¶ŠµŃŃ‚ŠŗŠ¾Š³Š¾ Š“ŠøсŠŗŠ° Š“Š»Ń этŠ¾Š³Š¾ шŠøфрŠ° ŠµŃ‰Šµ Š½Šµ рŠµŠ°Š»ŠøŠ·Š¾Š²Š°Š½Š°! + + + + Failed to encrypt hard disk + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°ŃˆŠøфрŠ¾Š²Š°Ń‚ŃŒ Š¶ŠµŃŃ‚ŠŗŠøŠ¹ Š“ŠøсŠŗ + + + + Failed to encrypt hard disk! + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°ŃˆŠøфрŠ¾Š²Š°Ń‚ŃŒ Š¶ŠµŃŃ‚ŠŗŠøŠ¹ Š“ŠøсŠŗ! + + + + Invalid number of arguments: %d + ŠŠµŠæрŠ°Š²ŠøŠ»ŃŒŠ½Š¾Šµ ŠŗŠ¾Š»ŠøчŠµŃŃ‚Š²Š¾ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚Š¾Š²: %d + + + + Enter password: + Š’Š²ŠµŠ“ŠøтŠµ ŠæŠ°Ń€Š¾Š»ŃŒ: + + + + Failed to read password from file + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾Ń‡ŠµŃŃ‚ŃŒ ŠæŠ°Ń€Š¾Š»ŃŒ ŠøŠ· фŠ°Š¹Š»Š° + + + + The given password is correct + + Š£ŠŗŠ°Š·Š°Š½Š½Ń‹Š¹ ŠæŠ°Ń€Š¾Š»ŃŒ - ŠŗŠ¾Ń€Ń€ŠµŠŗтŠ½Ń‹Š¹ + + + + + No medium specified! + ŠŠµ уŠŗŠ°Š·Š°Š½ Š½Š¾ŃŠøтŠµŠ»ŃŒ! + + + + Enter encryption password: + Š’Š²ŠµŠ“ŠøтŠµ ŠæŠ°Ń€Š¾Š»ŃŒ Š“Š»Ń шŠøфрŠ¾Š²Š°Š½Šøя: + + + + + Error opening '%s' for writing: %Rrc + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š¾Ń‚Šŗрыть '%s' Š“Š»Ń Š·Š°ŠæŠøсŠø: %Rrc + + + + Specified offset (%#RX64) is beyond the end of the medium (%#RX64) + Š£ŠŗŠ°Š·Š°Š½Š½Š°Ń ŠæŠ¾Š·ŠøцŠøя (%#RX64) Š²Ń‹Ń…Š¾Š“Šøт Š·Š° Š³Ń€Š°Š½Šøцу Š½Š¾ŃŠøтŠµŠ»Ń (%#RX64) + + + + Read(%zu bytes at %#RX64) + + Š§Ń‚ŠµŠ½ŠøŠµ (%zu Š±Š°Š¹Ń‚, ŠæŠ¾Š·ŠøцŠøя %#RX64) + Š§Ń‚ŠµŠ½ŠøŠµ (%zu Š±Š°Š¹Ń‚Š°, ŠæŠ¾Š·ŠøцŠøя %#RX64) + Š§Ń‚ŠµŠ½ŠøŠµ (%zu Š±Š°Š¹Ń‚, ŠæŠ¾Š·ŠøцŠøя %#RX64) + + + + + ********** <ditto x %RU64> + + ********** <тŠ¾ Š¶Šµ сŠ°Š¼Š¾Šµ x %RU64> + + + + + + Error writing to '%s': %Rrc + ŠžŃˆŠøŠ±ŠŗŠ° ŠæрŠø Š·Š°ŠæŠøсŠø Š² '%s': %Rrc + + + + Expected read() at offset %RU64 (%#RX64) to return %#zx bytes, only got %#zx! + + + ŠžŠ¶ŠøŠ“Š°Š»Š¾ŃŃŒ read() с ŠæŠ¾Š·Ń†ŠøŠø %RU64 (%#RX64) ŠæрŠ¾Ń‡ŠøтŠ°Ń‚ŃŒ %#zx Š±Š°Š¹Ń‚, ŠæŠ¾Š»ŃƒŃ‡ŠµŠ½Š¾ тŠ¾Š»ŃŒŠŗŠ¾ %#zx! + + ŠžŠ¶ŠøŠ“Š°Š»Š¾ŃŃŒ read() с ŠæŠ¾Š·Ń†ŠøŠø %RU64 (%#RX64) ŠæрŠ¾Ń‡ŠøтŠ°Ń‚ŃŒ %#zx Š±Š°Š¹Ń‚Š°, ŠæŠ¾Š»ŃƒŃ‡ŠµŠ½Š¾ тŠ¾Š»ŃŒŠŗŠ¾ %#zx! + + ŠžŠ¶ŠøŠ“Š°Š»Š¾ŃŃŒ read() с ŠæŠ¾Š·Ń†ŠøŠø %RU64 (%#RX64) ŠæрŠ¾Ń‡ŠøтŠ°Ń‚ŃŒ %#zx Š±Š°Š¹Ń‚, ŠæŠ¾Š»ŃƒŃ‡ŠµŠ½Š¾ тŠ¾Š»ŃŒŠŗŠ¾ %#zx! + + + + + + + Error closing '%s': %Rrc + ŠžŃˆŠøŠ±ŠŗŠ° ŠæрŠø Š·Š°ŠŗрытŠøŠø '%s': %Rrc + + + + GuestCtrl + + + Unable to install console control handler, rc=%Rrc + + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ устŠ°Š½Š¾Š²Šøть Š¾Š±Ń€Š°Š±Š¾Ń‚чŠøŠŗ уŠæрŠ°Š²Š»ŠµŠ½Šøя ŠŗŠ¾Š½ŃŠ¾Š»Šø, rc=%Rrc + + + + + Unable to uninstall console control handler, rc=%Rrc + + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ уŠ“Š°Š»Šøть Š¾Š±Ń€Š°Š±Š¾Ń‚чŠøŠŗ уŠæрŠ°Š²Š»ŠµŠ½Šøя ŠŗŠ¾Š½ŃŠ¾Š»Šø, rc=%Rrc + + + + + + starting + Š·Š°ŠæусŠŗ + + + + + + started + Š·Š°ŠæущŠµŠ½ + + + + paused + ŠæрŠøŠ¾ŃŃ‚Š°Š½Š¾Š²Š»ŠµŠ½ + + + + + terminating + Š·Š°Š²ŠµŃ€ŃˆŠµŠ½ŠøŠµ + + + + successfully terminated + усŠæŠµŃˆŠ½Š¾ Š·Š°Š²ŠµŃ€ŃˆŠµŠ½ + + + + terminated by signal + Š·Š°Š²ŠµŃ€ŃˆŠµŠ½ ŠæŠ¾ сŠøŠ³Š½Š°Š»Ńƒ + + + + abnormally aborted + Š½ŠµŠ½Š¾Ń€Š¼Š°Š»ŃŒŠ½Š¾ ŠæрŠµŃ€Š²Š°Š½ + + + + + + timed out + ŠæрŠµŠ²Ń‹ŃˆŠµŠ½Š¾ Š²Ń€ŠµŠ¼Ń Š¾Š¶ŠøŠ“Š°Š½Šøя + + + + + timed out, hanging + ŠæрŠµŠ²Ń‹ŃˆŠµŠ½Š¾ Š²Ń€ŠµŠ¼Ń Š¾Š¶ŠøŠ“Š°Š½Šøя, Š·Š°Š²Šøс + + + + + + killed + уŠ±Šøт + + + + + + + error + Š¾ŃˆŠøŠ±ŠŗŠ° + + + + + + + + + unknown + Š½ŠµŠøŠ·Š²ŠµŃŃ‚Š½Š¾ + + + + + terminated + Š·Š°Š²ŠµŃ€ŃˆŠµŠ½ + + + + status changed + стŠ°Ń‚ŃƒŃ ŠøŠ·Š¼ŠµŠ½ŠµŠ½ + + + + stdin ready + stdin Š³Š¾Ń‚Š¾Š² + + + + data on stdout + Š“Š°Š½Š½Ń‹Šµ Š² stdout + + + + data on stderr + Š“Š°Š½Š½Ń‹Šµ Š² stderr + + + + waiting flag not supported + фŠ»Š°Š³ Š¾Š¶ŠøŠ“Š°Š½Šøя Š½Šµ ŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŃ‚ся + + + + opening + Š¾Ń‚ŠŗрыŠ²Š°ŠµŃ‚ся + + + + open + Š¾Ń‚Šŗрыт + + + + closing + Š·Š°ŠŗрыŠ²Š°ŠµŃ‚ся + + + + closed + Š·Š°Šŗрыт + + + + fifo + fifo + + + + char-device + сŠøŠ¼Š²Š¾Š»ŃŒŠ½Š¾Šµ устрŠ¾Š¹ŃŃ‚Š²Š¾ + + + + directory + Š“ŠøрŠµŠŗтŠ¾Ń€Šøя + + + + block-device + Š±Š»Š¾Ń‡Š½Š¾Šµ устрŠ¾Š¹ŃŃ‚Š²Š¾ + + + + file + фŠ°Š¹Š» + + + + symlink + сŠøŠ²Š¾Š»ŠøчŠµŃŠŗŠ°Ń ссыŠ»ŠŗŠ° + + + + socket + сŠ¾ŠŗŠµŃ‚ + + + + white-out + white-out + + + + Error details: + ŠŸŠ¾Š“рŠ¾Š±Š½Š¾ŃŃ‚Šø Š¾ŃˆŠøŠ±ŠŗŠø: + + + + Object has indicated no error (%Rhrc)!? + + ŠžŠ±ŃŠŠµŠŗт Š½Šµ ŠæŠ¾ŠŗŠ°Š·Š°Š» Š¾ŃˆŠøŠ±ŠŗŠø (%Rhrc)!? + + + + + Could not lookup progress information + + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š½Š°Š¹Ń‚Šø ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†Šøю Š¾ ŠæрŠ¾Š³Ń€ŠµŃŃŠµ + + + + + + Out of memory + ŠŠµ хŠ²Š°Ń‚Š°ŠµŃ‚ ŠæŠ°Š¼ŃŃ‚Šø + + + + The --username|-u option is ignored by '%s' + ŠžŠæцŠøя --username|-u ŠæрŠ¾ŠøŠ³Š½Š¾Ń€ŠøрŠ¾Š²Š°Š½Š° '%s' + + + + Password is given more than once. + ŠŸŠ°Ń€Š¾Š»ŃŒ уŠŗŠ°Š·Š°Š½ Š½ŠµŃŠŗŠ¾Š»ŃŒŠŗŠ¾ рŠ°Š·. + + + + The --password option is ignored by '%s' + ŠžŠæцŠøя --password ŠæрŠ¾ŠøŠ³Š½Š¾Ń€ŠøрŠ¾Š²Š°Š½Š° '%s' + + + + The --password-file|-p option is ignored by '%s' + ŠžŠæцŠøя --password-file|-p ŠæрŠ¾ŠøŠ³Š½Š¾Ń€ŠøрŠ¾Š²Š°Š½Š° '%s' + + + + The --domain option is ignored by '%s' + ŠžŠæцŠø --domain ŠæрŠ¾ŠøŠ³Š½Š¾Ń€ŠøрŠ¾Š²Š°Š½Š° '%s' + + + + Failed to get a IConsole pointer for the machine. Is it still running? + + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š»ŃƒŃ‡Šøть уŠŗŠ°Š·Š°Ń‚ŠµŠ»ŃŒ Š½Š° IConsole Š“Š»Ń Š¼Š°ŃˆŠøŠ½Ń‹. ŠžŠ½Š° Š²ŃŠµ ŠµŃ‰Šµ рŠ°Š±Š¾Ń‚Š°ŠµŃ‚? + + + + + Machine "%s" is not running (currently %s)! + + ŠœŠ°ŃˆŠøŠ½Š° "%s" Š½Šµ Š·Š°ŠæущŠµŠ½Š° (тŠµŠŗущŠøŠ¹: %s)! + + + + + [%RU32] VBoxManage Guest Control [%s] - %s + [%RU32] VBoxManage Š“Š¾ŃŃ‚ŠµŠ²Š¾Šµ Š£ŠæрŠ°Š²Š»ŠµŠ½ŠøŠµ [%s] - %s + + + + No enough memory for session name + ŠŠµŠ“Š¾ŃŃ‚Š°Ń‚Š¾Ń‡Š½Š¾ ŠæŠ°Š¼ŃŃ‚Šø Š“Š»Ń ŠøŠ¼ŠµŠ½Šø сŠµŃŃŠøŠø + + + + Creating guest session as user '%s'... + + Š”Š¾Š·Š“Š°Š½ŠøŠµ Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ сŠµŃŃŠøŠø ŠæŠ¾Š“ ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»ŠµŠ¼ '%s'... + + + + + Out of memory setting up IGuest::CreateSession call + ŠŠµ хŠ²Š°Ń‚Š°ŠµŃ‚ ŠæŠ°Š¼ŃŃ‚Šø Š“Š»Ń Š½Š°ŃŃ‚Ń€Š¾Š¹ŠŗŠø Š²Ń‹Š·Š¾Š²Š° IGuest::CreateSession + + + + Waiting for guest session to start... + + ŠžŠ¶ŠøŠ“Š°Š½ŠøŠµ стŠ°Ń€Ń‚Š° Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ сŠµŃŃŠøŠø... + + + + + Out of memory setting up IGuestSession::WaitForArray call + ŠŠµ хŠ²Š°Ń‚Š°ŠµŃ‚ ŠæŠ°Š¼ŃŃ‚Šø Š“Š»Ń Š½Š°ŃŃ‚Ń€Š¾Š¹ŠŗŠø Š²Ń‹Š·Š¾Š²Š° IGuestSession::WaitForArray + + + + Successfully started guest session (ID %RU32) + + Š£ŃŠæŠµŃˆŠ½Š¾ Š·Š°ŠæущŠµŠ½Š° Š³Š¾ŃŃ‚ŠµŠ²Š°Ń сŠµŃŃŠøя (ID %RU32) + + + + + Error starting guest session (current status is: %s) + + ŠžŃˆŠøŠ±ŠŗŠ° Š²Š¾ Š²Ń€ŠµŠ¼Ń стŠ°Ń€Ń‚Š° Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ сŠµŃŃŠøŠø (тŠµŠŗущŠøŠ¹ стŠ°Ń‚ŃƒŃ: %s) + + + + + <unknown> + <Š½ŠµŠøŠ·Š²ŠµŃŃ‚Š½Š¾> + + + + No user name specified! + ŠŠµ уŠŗŠ°Š·Š°Š½Š¾ ŠøŠ¼Ń ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Ń! + + + + Closing guest session ... + + Š—Š°ŠŗрытŠøŠµ Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ сŠµŃŃŠøŠø ... + + + + + Guest session detached + + Š“Š¾ŃŃ‚ŠµŠ²Š°Ń сŠµŃŃŠøя Š¾Ń‚сŠ¾ŠµŠ“ŠøŠ½ŠµŠ½Š° + + + + + Unable to write output, rc=%Rrc + + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š·Š°ŠæŠøсŠ°Ń‚ŃŒ Š²Ń‹Ń…Š¾Š“Š½Š¾Š¹ ŠæŠ¾Ń‚Š¾Šŗ, rc=%Rrc + + + + + Unsupported %s line ending conversion + ŠŠµŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŠ¼Š¾Šµ ŠæрŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Š½ŠøŠµ %s Š·Š°Š²ŠµŃ€ŃˆŠµŠ½Šøя стрŠ¾ŠŗŠø + + + + Error getting %s handle: %Rrc + ŠžŃˆŠøŠ±ŠŗŠ° ŠæрŠø ŠæŠ¾Š»ŃƒŃ‡ŠµŠ½ŠøŠø %s Š¾Š±Ń€Š°Š±Š¾Ń‚чŠøŠŗŠ°: %Rrc + + + + Invalid argument variable[=value]: '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š°Ń ŠæŠµŃ€ŠµŠ¼ŠµŠ½Š½Š°Ń Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚Š°[=Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ]: '%s' + + + + Warning: Deprecated option "--no-profile" specified + + ŠŸŃ€ŠµŠ“уŠæрŠµŠ¶Š“ŠµŠ½ŠøŠµ: Š£ŠŗŠ°Š·Š°Š½Š° устŠ°Ń€ŠµŠ²ŃˆŠ°Ń Š¾ŠæцŠøя "--no-profile" + + + + + No executable specified! + ŠŠµ уŠŗŠ°Š·Š°Š½ Š²Ń‹ŠæŠ¾Š»Š½ŃŠµŠ¼Ń‹Š¹ фŠ°Š¹Š»! + + + + Starting guest process ... + + Š”тŠ°Ń€Ń‚ Š³Š¾ŃŃ‚ŠµŠ²Š¾Š³Š¾ ŠæрŠ¾Ń†ŠµŃŃŠ°... + + + + + Starting guest process (within %ums) + + Š”тŠ°Ń€Ń‚ Š³Š¾ŃŃ‚ŠµŠ²Š¾Š³Š¾ ŠæрŠ¾Ń†ŠµŃŃŠ° (Š² тŠµŃ‡ŠµŠ½ŠøŠµ %u Š¼ŃŠµŠŗ) + + + + + Process '%s' (PID %RU32) started + + ŠŸŃ€Š¾Ń†ŠµŃŃ '%s' (PID %RU32) Š·Š°ŠæущŠµŠ½ + + + + + [%RU32 - Session %RU32] + + [%RU32 - Š”ŠµŃŃŠøя %RU32] + + + + + waitResult: %d + + рŠµŠ·ŃƒŠ»ŃŒŃ‚Š°Ń‚ Š¾Š¶ŠøŠ“Š°Š½Šøя: %d + + + + + Process terminated + + ŠŸŃ€Š¾Ń†ŠµŃŃ Š·Š°Š²ŠµŃ€ŃˆŠµŠ½ + + + + + Process execution aborted! + + Š’Ń‹ŠæŠ¾Š»Š½ŠµŠ½ŠøŠµ ŠæрŠ¾Ń†ŠµŃŃŠ° ŠæрŠµŃ€Š²Š°Š½Š¾! + + + + + Process successfully started! + + ŠŸŃ€Š¾Ń†ŠµŃŃ усŠæŠµŃˆŠ½Š¾ Š·Š°ŠæущŠµŠ½! + + + + + Exit code=%u (Status=%u [%s]) + + ŠšŠ¾Š“ Š²Ń‹Ń…Š¾Š“Š° =%u (Š”тŠ°Ń‚ŃƒŃ =%u [%s]) + + + + + Process timed out (guest side) and %s + + ŠŸŃ€ŠµŠ²Ń‹ŃˆŠµŠ½Š¾ Š²Ń€ŠµŠ¼Ń Š¾Š¶ŠøŠ“Š°Š½Šøя ŠæрŠ¾Ń†ŠµŃŃŠ° (Š³Š¾ŃŃ‚ŠµŠ²Š°Ń стŠ¾Ń€Š¾Š½Š°) Šø %s + + + + + failed to terminate so far + Š½Šµ уŠ“Š°Š»Š¾ŃŃŒ Š“Š¾ сŠøх ŠæŠ¾Ń€ Š·Š°Š²ŠµŃ€ŃˆŠøть + + + + was terminated + Š·Š°Š²ŠµŃ€ŃˆŠµŠ½ + + + + Process now is in status [%s] (unexpected) + + Š”ŠµŠ¹Ń‡Š°Ń ŠæрŠ¾Ń†ŠµŃŃ Š² стŠ°Ń‚ŃƒŃŠµ [%s] (Š½ŠµŠ¾Š¶ŠøŠ“Š°Š½Š½Š¾) + + + + + Process monitor loop quit with vrc=%Rrc + + Š¦ŠøŠŗŠ» Š½Š°Š±Š»ŃŽŠ“ŠµŠ½Šøя Š·Š° ŠæрŠ¾Ń†ŠµŃŃŠ¾Š¼ Š·Š°ŠŗŠ¾Š½Ń‡ŠøŠ»ŃŃ с vrc=%Rrc + + + + + Process monitor loop timed out + + ŠŸŃ€ŠµŠ²Ń‹ŃˆŠµŠ½Š¾ Š²Ń€ŠµŠ¼Ń Š¾Š¶ŠøŠ“Š°Š½Šøя Š² цŠøŠŗŠ»Šµ Š½Š°Š±Š»ŃŽŠ“ŠµŠ½Šøя Š·Š° ŠæрŠ¾Ń†ŠµŃŃŠ¾Š¼ + + + + + No sources specified! + ŠŠµ уŠŗŠ°Š·Š°Š½ ŠøстŠ¾Ń‡Š½ŠøŠŗ! + + + + + No destination specified! + ŠŠµ уŠŗŠ°Š·Š°Š½Š¾ Š½Š°Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ! + + + + + RTPathAbs failed on '%s': %Rrc + RTPathAbs Š·Š°Š²ŠµŃ€ŃˆŠøŠ»ŃŃ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹ Š½Š° '%s': %Rrc + + + + Copying from host to guest ... + + ŠšŠ¾ŠæŠøрŠ¾Š²Š°Š½ŠøŠµ ŠøŠ· хŠ¾ŃŃ‚Š° Š² Š³Š¾ŃŃ‚ŠµŠ²ŃƒŃŽ сŠøстŠµŠ¼Ńƒ... + + + + + Copying from guest to host ... + + ŠšŠ¾ŠæŠøрŠ¾Š²Š°Š½ŠøŠµ ŠøŠ· Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ сŠøстŠµŠ¼Ń‹ Š² хŠ¾ŃŃ‚... + + + + + + File '%s' -> '%s' + + Š¤Š°Š¹Š» '%s' -> '%s' + + + + + + Directory '%s' -> '%s' + + Š”ŠøрŠµŠŗтŠ¾Ń€Šøя '%s' -> '%s' + + + + + + Not a file or directory: %s + + ŠŠµ фŠ°Š¹Š» Šø Š½Šµ Š“ŠøрŠµŠŗтŠ¾Ń€Šøя: %s + + + + + + RTPathQueryInfo failed on '%s': %Rrc + RTPathQueryInfo Š·Š°Š²ŠµŃ€ŃˆŠµŠ½ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹ Š½Š° '%s': %Rrc + + + + Destination must be a directory! + ŠŠ°Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ Š“Š¾Š»Š¶Š½Š¾ Š±Ń‹Ń‚ŃŒ Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠµŠ¹! + + + + FsObjQueryInfo failed on '%s': %Rhrc + FsObjQueryInfo Š·Š°Š²ŠµŃ€ŃˆŠµŠ½ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹ Š½Š° '%s': %Rhrc + + + + File copy failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠŗŠ¾ŠæŠøрŠ¾Š²Š°Ń‚ŃŒ фŠ°Š¹Š» + + + + Creating %RU32 directories... + + + Š”Š¾Š·Š“Š°Š½ŠøŠµ %RU32 Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠø... + + Š”Š¾Š·Š“Š°Š½ŠøŠµ %RU32 Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠ¹... + + Š”Š¾Š·Š“Š°Š½ŠøŠµ %RU32 Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠ¹... + + + + + + mkdir was interrupted by Ctrl-C (%u left) + + mkdir ŠæрŠµŃ€Š²Š°Š½ чŠµŃ€ŠµŠ· Ctrl-C (%u Š¾ŃŃ‚Š°Š»Š¾ŃŃŒ) + + + + + Creating directory "%s" ... + + Š”Š¾Š·Š“Š°Š½ŠøŠµ Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠø "%s" ... + + + + + + + Out of memory + + ŠŠµ хŠ²Š°Ń‚Š°ŠµŃ‚ ŠæŠ°Š¼ŃŃ‚Šø + + + + + No directory to create specified! + ŠŠµ уŠŗŠ°Š·Š°Š½Š° Š“ŠøрŠµŠŗтŠ¾Ń€Šøя Š“Š»Ń сŠ¾Š·Š“Š°Š½Šøя! + + + + Removing %RU32 directory tree(s)... + + + Š£Š“Š°Š»ŠµŠ½ŠøŠµ %RU32 Š“ŠµŃ€ŠµŠ²Š° Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠ¹... + + Š£Š“Š°Š»ŠµŠ½ŠøŠµ %RU32 Š“ŠµŃ€ŠµŠ²ŃŒŠµŠ² Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠ¹... + + Š£Š“Š°Š»ŠµŠ½ŠøŠµ %RU32 Š“ŠµŃ€ŠµŠ²ŃŒŠµŠ² Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠ¹... + + + + + + Removing %RU32 directorie(s)... + + + Š£Š“Š°Š»ŠµŠ½ŠøŠµ %RU32 Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠø... + + Š£Š“Š°Š»ŠµŠ½ŠøŠµ %RU32 Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠ¹... + + Š£Š“Š°Š»ŠµŠ½ŠøŠµ %RU32 Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠ¹... + + + + + + rmdir was interrupted by Ctrl-C (%u left) + + rmdir ŠæрŠµŃ€Š²Š°Š½ чŠµŃ€ŠµŠ· Ctrl-C (%u Š¾ŃŃ‚Š°Š»Š¾ŃŃŒ) + + + + + Removing directory "%s" ... + + Š£Š“Š°Š»ŠµŠ½ŠøŠµ Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠø "%s" ... + + + + + Recursively removing directory "%s" ... + + Š ŠµŠŗурсŠøŠ²Š½Š¾Šµ уŠ“Š°Š»ŠµŠ½ŠøŠµ Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠø "%s" ... + + + + + Directory deletion failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ уŠ“Š°Š»Šøть Š“ŠøрŠµŠŗтŠ¾Ń€Šøю + + + + Out of memory during recursive rmdir + + ŠŠµ хŠ²Š°Ń‚Š°ŠµŃ‚ ŠæŠ°Š¼ŃŃ‚Šø Š“Š»Ń рŠµŠŗурсŠøŠ²Š½Š¾Š³Š¾ rmdir + + + + + No directory to remove specified! + ŠŠµ уŠŗŠ°Š·Š°Š½Š° Š“ŠøрŠµŠŗтŠ¾Ń€Šøя Š“Š»Ń уŠ“Š°Š»ŠµŠ½Šøя! + + + + Removing %RU32 file(s)... + + + Š£Š“Š°Š»ŠµŠ½ŠøŠµ %RU32 фŠ°Š¹Š»Š°... + + Š£Š“Š°Š»ŠµŠ½ŠøŠµ %RU32 фŠ°Š¹Š»Š¾Š²... + + Š£Š“Š°Š»ŠµŠ½ŠøŠµ %RU32 фŠ°Š¹Š»Š¾Š²... + + + + + + rm was interrupted by Ctrl-C (%u left) + + rm ŠæрŠµŃ€Š²Š°Š½ чŠµŃ€ŠµŠ· Ctrl-C (%u Š¾ŃŃ‚Š°Š»Š¾ŃŃŒ) + + + + + Removing file "%s" ... + + Š£Š“Š°Š»ŠµŠ½ŠøŠµ фŠ°Š¹Š»Š° "%s" ... + + + + + No file to remove specified! + ŠŠµ уŠŗŠ°Š·Š°Š½ фŠ°Š¹Š» Š“Š»Ń уŠ“Š°Š»ŠµŠ½Šøя! + + + + Failed to initialize, rc=%Rrc + + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾ŠøŠ½ŠøцŠøŠ°Š»ŠøŠ·ŠøрŠ¾Š²Š°Ń‚ŃŒ, rc=%Rrc + + + + + No source(s) to move specified! + ŠŠµ уŠŗŠ°Š·Š°Š½ ŠøстŠ¾Ń‡Š½ŠøŠŗ(Šø) Š“Š»Ń ŠæŠµŃ€ŠµŠ¼ŠµŃ‰ŠµŠ½Šøя! + + + + Destination does not exist + + ŠŠ°Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ Š½Šµ сущŠµŃŃ‚Š²ŃƒŠµŃ‚ + + + + + Destination must be a directory when specifying multiple sources + + ŠŠ°Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ Š“Š¾Š»Š¶Š½Š¾ Š±Ń‹Ń‚ŃŒ Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠµŠ¹ ŠŗŠ¾Š³Š“Š° уŠŗŠ°Š·Ń‹Š²Š°ŃŽŃ‚ся Š½ŠµŃŠŗŠ¾Š»ŃŒŠŗŠ¾ ŠøстŠ¾Ń‡Š½ŠøŠŗŠ¾Š² + + + + + Unable to determine destination type: %Rhrc + + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š¾ŠæрŠµŠ“ŠµŠ»Šøть тŠøŠæ Š½Š°Š·Š½Š°Ń‡ŠµŠ½Šøя: %Rhrc + + + + + Renaming %RU32 %s ... + + ŠŸŠµŃ€ŠµŠøŠ¼ŠµŠ½Š¾Š²Š°Š½ŠøŠµ %RU32 %s ... + + + + + sources + + ŠøстŠ¾Ń‡Š½ŠøŠŗŠ° + ŠøстŠ¾Ń‡Š½ŠøŠŗŠ¾Š² + ŠøстŠ¾Ń‡Š½ŠøŠŗŠ¾Š² + + + + + source + ŠøстŠ¾Ń‡Š½ŠøŠŗŠ° + + + + Cannot stat "%s": No such file or directory + + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæŠ¾Š»ŃƒŃ‡Šøть ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†Šøю "%s": ŠŠµŃ‚ тŠ°ŠŗŠ¾Š³Š¾ фŠ°Š¹Š»Š° ŠøŠ»Šø Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠø + + + + + Renaming %s "%s" to "%s" ... + + ŠŸŠµŃ€ŠµŠøŠ¼ŠµŠ½Š¾Š²Š°Š½ŠøŠµ %s "%s" Š² "%s" ... + + + + + directory + object + Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠø + + + + file + object + фŠ°Š¹Š»Š° + + + + Warning: Not all sources were renamed + + ŠŸŃ€ŠµŠ“уŠæрŠµŠ¶Š“ŠµŠ½ŠøŠµ: ŠŠµ Š²ŃŠµ ŠøстŠ¾Ń‡Š½ŠøŠŗŠø ŠæŠµŃ€ŠµŠøŠ¼ŠµŠ½Š¾Š²Š°Š½Ń‹ + + + + + More than one template specified! + + Š£ŠŗŠ°Š·Š°Š½Š¾ Š±Š¾Š»ŠµŠµ Š¾Š“Š½Š¾Š³Š¾ шŠ°Š±Š»Š¾Š½Š°! + + + + + No template specified! + ŠØŠ°Š±Š»Š¾Š½Ń‹ Š½Šµ уŠŗŠ°Š·Š°Š½Ń‹! + + + + Creating temporary files is currently not supported! + Š”Š¾Š·Š“Š°Š½ŠøŠµ Š²Ń€ŠµŠ¼ŠµŠ½Š½Ń‹Ń… фŠ°Š¹Š»Š¾Š² сŠµŠ¹Ń‡Š°Ń Š½Šµ ŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŃ‚ся! + + + + Creating temporary directory from template '%s' in directory '%s' ... + + Š”Š¾Š·Š“Š°Š½ŠøŠµ Š²Ń€ŠµŠ¼ŠµŠ½Š½Š¾Š¹ Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠø ŠøŠ· шŠ°Š±Š»Š¾Š½Š° '%s' Š² Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠø '%s' ... + + + + + Creating temporary directory from template '%s' in default temporary directory ... + + Š”Š¾Š·Š“Š°Š½ŠøŠµ Š²Ń€ŠµŠ¼ŠµŠ½Š½Š¾Š¹ Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠø ŠøŠ· шŠ°Š±Š»Š¾Š½Š° '%s' Š²Š¾ Š²Ń€ŠµŠ¼ŠµŠ½Š½Š¾Š¹ Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠø ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю... + + + + + Creating temporary file from template '%s' in directory '%s' ... + + Š”Š¾Š·Š“Š°Š½ŠøŠµ Š²Ń€Š¼ŠµŠ½Š½Š¾Š³Š¾ фŠ°Š¹Š»Š° ŠøŠ· шŠ°Š±Š»Š¾Š½Š° '%s' Š² Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠø '%s' ... + + + + + Creating temporary file from template '%s' in default temporary directory ... + + Š”Š¾Š·Š“Š°Š½ŠøŠµ Š²Ń€ŠµŠ¼ŠµŠ½Š½Š¾Š³Š¾ фŠ°Š¹Š»Š° ŠøŠ· шŠ°Š±Š»Š¾Š½Š° '%s' Š²Š¾ Š²Ń€ŠµŠ¼ŠµŠ½Š½Š¾Š¹ Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠø ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю... + + + + + Directory name: %ls + + Š˜Š¼Ń Š“ŠøрŠµŠŗтŠ¾Ń€ŠøŠø: %ls + + + + + Command "%s" not implemented yet! + ŠšŠ¾Š¼Š°Š½Š“Š° "%s" ŠµŃ‰Šµ Š½Šµ рŠµŠ°Š»ŠøŠ·Š¾Š²Š°Š½Š°! + + + + Nothing to stat! + ŠŠµŃ‚ Š¾Š±ŃŠŠµŠŗтŠ° Š“Š»Ń Š·Š°ŠæрŠ¾ŃŠ° ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŠø! + + + + Checking for element "%s" ... + + ŠŸŃ€Š¾Š²ŠµŃ€ŠŗŠ° эŠ»ŠµŠ¼ŠµŠ½Ń‚Š° "%s" ... + + + + + Failed to stat '%s': No such file + + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š»ŃƒŃ‡Šøть ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†Šøю Š“Š»Ń '%s': ŠŠµŃ‚ тŠ°ŠŗŠ¾Š³Š¾ фŠ°Š¹Š»Š° + + + + + File: '%s' + + Š¤Š°Š¹Š»: '%s' + + + + + Size: %-17RU64 Alloc: %-19RU64 Type: %s + + Š Š°Š·Š¼ŠµŃ€: %-17RU64 Š’Ń‹Š“ŠµŠ»ŠµŠ½Š¾: %-19RU64 Š¢ŠøŠæ: %s + + + + + Device: %#-17RX32 INode: %-18RU64 Links: %u + + Š£ŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š¾: %#-17RX32 INode: %-18RU64 Š”сыŠ»ŠŗŠø: %u + + + + + Mode: %-16s Attrib: %-17s Dev ID: %#RX32 + + Š ŠµŠ¶ŠøŠ¼: %-16s ŠŃ‚Ń‚Ń€ŠøŠ±ŃƒŃ‚Ń‹: %-17s ID устрŠ¾Š¹ŃŃ‚Š²Š°: %#RX32 + + + + + Mode: %-16s Attrib: %s + + Š ŠµŠ¶ŠøŠ¼: %-16s ŠŃ‚Ń‚Ń€ŠøŠ±ŃƒŃ‚Ń‹: %s + + + + + Owner: %4d/%-12ls Group: %4d/%ls + + Š’Š»Š°Š“ŠµŠ»ŠµŃ†: %4d/%-12ls Š“Ń€ŃƒŠæŠæŠ°: %4d/%ls + + + + + Birth: %s + + Š”Š¾Š·Š“Š°Š½: %s + + + + + Change: %s + + Š˜Š·Š¼ŠµŠ½ŠµŠ½: %s + + + + + Modify: %s + + ŠœŠ¾Š“ŠøфŠøцŠøрŠ¾Š²Š°Š½: %s + + + + + Access: %s + + Š”Š¾ŃŃ‚ŃƒŠæ: %s + + + + + Current run level is %RU32 + + Š¢ŠµŠŗущŠøŠ¹ урŠ¾Š²ŠµŠ½ŃŒ Š²Ń‹ŠæŠ¾Š»Š½ŠµŠ½Šøя %RU32 + + + + + Waiting for run level %RU32 ... + + ŠžŠ¶ŠøŠ“Š°Š½ŠøŠµ урŠ¾Š²Š½Ń Š²Ń‹ŠæŠ¾Š»Š½ŠµŠ½Šøя %RU32 ... + + + + + + Waiting failed with %Rrc + + ŠžŠ¶ŠøŠ“Š°Š½ŠøŠµ Š·Š°Š²ŠµŃ€ŃˆŠµŠ½Š¾ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹ %Rrc + + + + + Run level %RU32 reached + + Š£Ń€Š¾Š²ŠµŠ½ŃŒ Š²Ń‹ŠæŠ¾Š»Š½ŠµŠ½Šøя %RU32 Š“Š¾ŃŃ‚ŠøŠ³Š½ŃƒŃ‚ + + + + + Run level %RU32 not reached within time + + Š£Ń€Š¾Š²ŠµŠ½ŃŒ Š²Ń‹ŠæŠ¾Š»Š½ŠµŠ½Šøя %RU32 Š½Šµ Š“Š¾ŃŃ‚ŠøŠ³Š½ŃƒŃ‚ Š·Š° Š¾Ń‚Š²ŠµŠ“ŠµŠ½Š½Š¾Šµ Š²Ń€ŠµŠ¼Ń + + + + + RTPathAbsCxx failed on '%s': %Rrc + RTPathAbsCxx Š·Š°Š²ŠµŃ€ŃˆŠøŠ»ŃŃ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹ '%s': %Rrc + + + + Updating Guest Additions ... + + ŠžŠ±Š½Š¾Š²Š»ŠµŠ½ŠøŠµ Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½ŠøŠ¹ Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ”... + + + + + No Guest Additions source found or specified, aborting + + ŠŠµ Š½Š°Š¹Š“ŠµŠ½ Šø Š½Šµ уŠŗŠ°Š·Š°Š½ ŠøстŠ¾Ń‡Š½ŠøŠŗ Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½ŠøŠ¹ Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ”, ŠæрŠµŃ€Ń‹Š²Š°Š½ŠøŠµ + + + + + Source "%s" does not exist! + + Š˜ŃŃ‚Š¾Ń‡Š½ŠøŠŗ "%s" Š½Šµ сущŠµŃŃ‚Š²ŃƒŠµŃ‚! + + + + + OS type: + Š¢ŠøŠæ ŠžŠ”: + + + + Additions run level: + Š£Ń€Š¾Š²ŠµŠ½ŃŒ Š²Ń‹ŠæŠ¾Š»Š½ŠµŠ½Šøя Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½ŠøŠ¹: + + + + Additions version: + Š’ŠµŃ€ŃŠøя Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½ŠøŠ¹: + + + + Using source: %s + + Š˜ŃŠæŠ¾Š»ŃŒŠ·ŃƒŠµŠ¼Ń‹Š¹ ŠøстŠ¾Ń‡Š½ŠøŠŗ: %s + + + + + Waiting for current Guest Additions inside VM getting ready for updating ... + + ŠžŠ¶ŠøŠ“Š°Š½ŠøŠµ Š³Š¾Ń‚Š¾Š²Š½Š¾ŃŃ‚Šø тŠµŠŗущŠøх Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½ŠøŠ¹ Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ” Š²Š½ŃƒŃ‚Ń€Šø Š’Šœ Š“Š»Ń Š¾Š±Š½Š¾Š²Š»ŠµŠ½Šøя... + + + + + Guest Additions %lsr%RU64 currently installed, waiting for Guest Additions installer to start ... + + Š”ŠµŠ¹Ń‡Š°Ń устŠ°Š½Š¾Š²Š»ŠµŠ½Ń‹ Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½Šøя Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ” %lsr%RU64, Š¾Š¶ŠøŠ“Š°Š½ŠøŠµ Š·Š°ŠæусŠŗŠ° ŠøŠ½ŃŃ‚Š°Š»Š»ŃŃ‚Š¾Ń€Š° Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½ŠøŠ¹ Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ”... + + + + + Guest Additions update failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š¾Š±Š½Š¾Š²Šøть Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½Šøя Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ” + + + + Guest Additions update successful. + + Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½Šøя Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ” усŠæŠµŃˆŠ½Š¾ Š¾Š±Š½Š¾Š²Š»ŠµŠ½Ń‹. + + + + + Rebooting guest ... + + ŠŸŠµŃ€ŠµŠ·Š°Š³Ń€ŃƒŠ·ŠŗŠ° Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ сŠøстŠµŠ¼Ń‹... + + + + + Current installed Guest Additions don't support automatic rebooting. Please reboot manually. + + Š£ŃŃ‚Š°Š½Š¾Š²Š»ŠµŠ½Š½Ń‹Šµ сŠµŠ¹Ń‡Š°Ń Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½Šøя Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ” Š½Šµ ŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŃŽŃ‚ Š°Š²Ń‚Š¾Š¼Š°Ń‚ŠøчсŠŗую ŠæŠµŃ€ŠµŠ·Š°Š³Ń€ŃƒŠ·Šŗу. ŠŸŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæŠµŃ€ŠµŠ·Š°Š³Ń€ŃƒŠ·ŠøтŠµ Š²Ń€ŃƒŃ‡Š½ŃƒŃŽ. + + + + + Waiting for new Guest Additions inside VM getting ready ... + + ŠžŠ¶ŠøŠ“Š°Š½ŠøŠµ Š³Š¾Ń‚Š¾Š²Š½Š¾ŃŃ‚Šø Š½Š¾Š²Ń‹Ń… Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½ŠøŠ¹ Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ” Š²Š½ŃƒŃ‚Ń€Šø Š’Šœ... + + + + + Verifying Guest Additions update ... + + ŠŸŃ€Š¾Š²ŠµŃ€ŠŗŠ° Š¾Š±Š½Š¾Š²Š»ŠµŠ½Šøя Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½ŠøŠ¹ Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ”... + + + + + Old Guest Additions: %ls%RU64 + + Š”тŠ°Ń€Ń‹Šµ Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½Šøя Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ”: %ls%RU64 + + + + + New Guest Additions: %ls%RU64 + + ŠŠ¾Š²Ń‹Šµ Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½Šøя Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ”: %ls%RU64 + + + + + +Error updating Guest Additions, please check guest installer log + + +ŠžŃˆŠøŠ±ŠŗŠ° ŠæрŠø Š¾Š±Š½Š¾Š²Š»ŠµŠ½ŠøŠø Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½ŠøŠ¹ Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ”, ŠæŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, ŠæрŠ¾Š²ŠµŃ€ŃŒŃ‚Šµ Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ Š¶ŃƒŃ€Š½Š°Š» устŠ°Š½Š¾Š²ŠŗŠø + + + + + +WARNING: Guest Additions were downgraded + + +ŠŸŠ Š•Š”Š£ŠŸŠ Š•Š–Š”Š•ŠŠ˜Š•: Š£ŃŃ‚Š°Š½Š¾Š²Š»ŠµŠ½Ń‹ Š±Š¾Š»ŠµŠµ стŠ°Ń€Ń‹Šµ Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½Šøя Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ” + + + + + The guest needs to be restarted in order to make use of the updated Guest Additions. + + Š“Š¾ŃŃ‚ŠµŠ²ŃƒŃŽ сŠøстŠµŠ¼Ńƒ Š½Š°Š“Š¾ ŠæŠµŃ€ŠµŠ·Š°Š³Ń€ŃƒŠ·Šøть Š“Š»Ń ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š½Šøя Š½Š¾Š²Ń‹Ń… Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½ŠøŠ¹ Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ”. + + + + + Invalid run level specified. Valid values are: system, userland, desktop + Š£ŠŗŠ°Š·Š°Š½ Š½ŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ урŠ¾Š²ŠµŠ½ŃŒ Š²Ń‹ŠæŠ¾Š»Š½ŠµŠ½Šøя. Š”Š¾ŠæустŠøŠ¼Ń‹Šµ Š·Š½Š°Ń‡ŠµŠ½Šøя: system, userland, desktop + + + + Missing run level to wait for + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ урŠ¾Š²ŠµŠ½ŃŒ Š²Ń‹ŠæŠ¾Š»Š½ŠµŠ½Šøя Š“Š»Ń Š¾Š¶ŠøŠ“Š°Š½Šøя + + + + Unknown list: '%s' + ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Ń‹Š¹ сŠæŠøсŠ¾Šŗ: '%s' + + + + Missing list name + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ ŠøŠ¼Ń сŠæŠøсŠŗŠ° + + + + Active guest sessions: + + ŠŠŗтŠøŠ²Š½Ń‹Šµ Š³Š¾ŃŃ‚ŠµŠ²Ń‹Šµ сŠµŃŃŠøŠø: + + + + + + Session #%-3zu ID=%-3RU32 User=%-16ls Status=[%s] Name=%ls + + Š”ŠµŃŃŠøя #%-3zu ID=%-3RU32 ŠŸŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»ŃŒ=%-16ls Š”тŠ°Ń‚ŃƒŃ=[%s] Š˜Š¼Ń=%ls + + + + + Process #%-03zu PID=%-6RU32 Status=[%s] Command=%ls + + ŠŸŃ€Š¾Ń†ŠµŃŃ#%-03zu PID=%-6RU32 Š”тŠ°Ń‚ŃƒŃ=[%s] ŠšŠ¾Š¼Š°Š½Š“Š°=%ls + + + + + File #%-03zu ID=%-6RU32 Status=[%s] Name=%ls + + Š¤Š°Š¹Š» #%-03zu ID=%-6RU32 Š”тŠ°Ń‚ŃƒŃ=[%s] Š˜Š¼Ń=%ls + + + + + +Total guest sessions: %zu + + + +Š’сŠµŠ³Š¾ Š³Š¾ŃŃ‚ŠµŠ²Ń‹Ń… сŠµŃŃŠøŠ¹: %zu + + + + + Total guest processes: %zu + + Š’сŠµŠ³Š¾ Š³Š¾ŃŃ‚ŠµŠ²Ń‹Ń… ŠæрŠ¾Ń†ŠµŃŃŠ¾Š²: %zu + + + + + Total guest files: %zu + + Š’сŠµŠ³Š¾ Š³Š¾ŃŃ‚ŠµŠ²Ń‹Ń… фŠ°Š¹Š»Š¾Š²: %zu + + + + + No active guest sessions found + + ŠŠµ Š½Š°Š¹Š“ŠµŠ½Š¾ Š°ŠŗтŠøŠ²Š½Ń‹Ń… Š³Š¾ŃŃ‚ŠµŠ²Ń‹Ń… сŠµŃŃŠøŠ¹ + + + + + Invalid PID value: 0 + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ PID: 0 + + + + Error parsing PID value: %Rrc + ŠžŃˆŠøŠ±ŠŗŠ° ŠæрŠø рŠ°Š·Š±Š¾Ń€Šµ Š·Š½Š°Ń‡ŠµŠ½Šøя PID: %Rrc + + + + At least one PID must be specified to kill! + ŠŃƒŠ¶Š½Š¾ уŠŗŠ°Š·Š°Ń‚ŃŒ хŠ¾Ń‚я Š±Ń‹ Š¾Š“ŠøŠ½ PID, ŠŗŠ¾Ń‚Š¾Ń€Ń‹Š¹ Š½Š°Š“Š¾ уŠ±Šøть! + + + + + No session ID specified! + ŠŠµ уŠŗŠ°Š·Š°Š½ ID сŠµŃŃŠøŠø! + + + + + Either session ID or name (pattern) must be specified + ŠŃƒŠ¶Š½Š¾ уŠŗŠ°Š·Š°Ń‚ŃŒ Š»ŠøŠ±Š¾ ID сŠµŃŃŠøŠø Š»ŠøŠ±Š¾ ŠøŠ¼Ń (шŠ°Š±Š»Š¾Š½) + + + + Terminating process (PID %RU32) (session ID %RU32) ... + + Š—Š°Š²ŠµŃ€ŃˆŠµŠ½ŠøŠµ ŠæрŠ¾Ń†ŠµŃŃŠ° (PID %RU32) (ID сŠµŃŃŠøŠø %RU32) ... + + + + + No matching process(es) for session ID %RU32 found + + ŠŠµ Š½Š°Š¹Š“ŠµŠ½Š¾ сŠ¾Š¾Ń‚Š²ŠµŃ‚стŠ²ŃƒŃŽŃ‰Šøх ŠæрŠ¾Ń†ŠµŃŃŠ¾Š² Š“Š»Ń ID сŠµŃŃŠøŠø %RU32 + + + + + No matching session(s) found + + ŠŠµ Š½Š°Š¹Š“ŠµŠ½Š¾ сŠ¾Š¾Ń‚Š²ŠµŃ‚стŠ²ŃƒŃŽŃ‰Šøх сŠµŃŃŠøŠ¹ + + + + + %RU32 process(es) terminated + + + Š—Š°Š²ŠµŃ€ŃˆŠµŠ½ %RU32 ŠæрŠ¾Ń†ŠµŃŃ + + Š—Š°Š²ŠµŃ€ŃˆŠµŠ½Š¾ %RU32 ŠæрŠ¾Ń†ŠµŃŃŠ° + + Š—Š°Š²ŠµŃ€ŃˆŠµŠ½Š¾ %RU32 ŠæрŠ¾Ń†ŠµŃŃŠ¾Š² + + + + + + Closing guest session ID=#%RU32 "%s" ... + + Š—Š°ŠŗрытŠøŠµ Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ сŠµŃŃŠøŠø ID=#%RU32 "%s"... + + + + + Guest session successfully closed + + Š“Š¾ŃŃ‚ŠµŠ²Š°Ń сŠµŃŃŠøя усŠæŠµŃˆŠ½Š¾ Š·Š°ŠŗрытŠ° + + + + + No guest session(s) found + + ŠŠµ Š½Š°Š¹Š“ŠµŠ½Š¾ Š³Š¾ŃŃ‚ŠµŠ²Ń‹Ń… сŠµŃŃŠøŠ¹ + + + + + Waiting for events ... + + ŠžŠ¶ŠøŠ“Š°Š½ŠøŠµ сŠ¾Š±Ń‹Ń‚ŠøŠ¹... + + + + + Unknown sub-command: '%s' + ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Š°Ń ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“Š°: '%s' + + + + Missing sub-command + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“Š° + + + + Missing VM name and sub-command + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ ŠøŠ¼Ń Š’Šœ Šø ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“Š° + + + + GuestCtrlLsnr + + + File ID=%RU32 "%s" changed status to [%s] + + Š¤Š°Š¹Š» с ID=%RU32 "%s" ŠøŠ·Š¼ŠµŠ½ŠøŠ» стŠ°Ń‚ŃƒŃ Š½Š° [%s] + + + + + Process PID=%RU32 "%s" changed status to [%s] + + ŠŸŃ€Š¾Ń†ŠµŃŃ PID=%RU32 "%s" ŠøŠ·Š¼ŠµŠ½ŠøŠ» стŠ°Ń‚ŃƒŃ Š½Š° [%s] + + + + + File "%s" %s + + Š¤Š°Š¹Š» "%s" %s + + + + + + + registered + рŠµŠ³ŠøстрŠ°Ń†Šøя сŠ“ŠµŠ»Š°Š½Š° + + + + + + unregistered + рŠµŠ³ŠøстрŠ°Ń†Šøя Š¾Ń‚Š¼ŠµŠ½ŠµŠ½Š° + + + + + + Registering ... + + Š ŠµŠ³ŠøстрŠ°Ń†Šøя... + + + + + Unregistering file ... + + ŠžŃ‚Š¼ŠµŠ½Š° рŠµŠ³ŠøстрŠ°Ń†ŠøŠø фŠ°Š¹Š»Š°... + + + + + Process "%s" %s + + ŠŸŃ€Š¾Ń†ŠµŃŃ "%s" %s + + + + + Unregistering process ... + + ŠžŃ‚Š¼ŠµŠ½Š° рŠµŠ³ŠøстрŠ°Ń†ŠøŠø ŠæрŠ¾Ń†ŠµŃŃŠ°... + + + + + Session ID=%RU32 "%s" changed status to [%s] + + Š”ŠµŃŃŠøя ID=%RU32 "%s" ŠøŠ·Š¼ŠµŠ½ŠøŠ»Š° стŠ°Ń‚ŃƒŃ Š½Š° [%s] + + + + + Session ID=%RU32 "%s" %s + + Š”ŠµŃŃŠøя ID=%RU32 "%s" %s + + + + + Unregistering ... + + ŠžŃ‚Š¼ŠµŠ½Š° рŠµŠ³ŠøстрŠ°Ń†ŠøŠø... + + + + + Reached run level %RU32 + + Š”Š¾ŃŃ‚ŠøŠ³Š½ŃƒŃ‚ урŠ¾Š²ŠµŠ½ŃŒ Š²Ń‹ŠæŠ¾Š»Š½ŠµŠ½Šøя %RU32 + + + + + GuestProp + + + + + + + + + Incorrect parameters + ŠŠµŠŗŠ¾Ń€Ń€ŠµŠŗтŠ½Ń‹Šµ ŠæŠ°Ń€Š°Š¼ŠµŃ‚ры + + + + No value set! + + ŠŠµ Š·Š°Š“Š°Š½Š¾ ŠŗŠ°ŠŗŠ¾Šµ-Š»ŠøŠ±Š¾ Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ! + + + + + Value: %ls + + Š—Š½Š°Ń‡ŠµŠ½ŠøŠµ: %ls + + + + + Timestamp: %lld + + Š’Ń€ŠµŠ¼Ń: %lld + + + + + Flags: %ls + + Š¤Š»Š°Š³Šø: %ls + + + + + No properties found. + + Š”Š²Š¾Š¹ŃŃ‚Š²Š° Š½Šµ Š½Š°Š¹Š“ŠµŠ½Ń‹. + + + + + Name: %ls, value: %ls, timestamp: %lld, flags: %ls + + Š˜Š¼Ń: %ls, Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ: %ls, Š²Ń€ŠµŠ¼Ń: %lld, фŠ»Š°Š³Šø: %ls + + + + + Property %ls was deleted + + + + + + Name: %ls, value: %ls, flags: %ls + + Š˜Š¼Ń: %ls, Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ: %ls, фŠ»Š°Š³Šø: %ls + + + + + Time out or interruption while waiting for a notification. + ŠŸŃ€ŠµŠ²Ń‹ŃˆŠµŠ½Š¾ Š²Ń€ŠµŠ¼Ń Š¾Š¶ŠøŠ“Š°Š½Šøя ŠøŠ»Šø ŠæрŠµŃ€Ń‹Š²Š°Š½ŠøŠµ Š²Š¾ Š²Ń€ŠµŠ¼Ń Š¾Š¶ŠøŠ“Š°Š½Šøя Š¾ŠæŠ¾Š²ŠµŃ‰ŠµŠ½Šøя. + + + + Help + + + Usage - %s%s: + + Š˜ŃŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š½ŠøŠµ - %s%s: + + + + + Usage: + + Š˜ŃŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š½ŠøŠµ: + + + + + No subcommand specified + ŠŠµ уŠŗŠ°Š·Š°Š½Š° ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“Š° + + + + Unknown subcommand: %s + ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Š°Ń ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“Š°: %s + + + + Too many parameters + Š”Š»ŠøшŠŗŠ¾Š¼ Š¼Š½Š¾Š³Š¾ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š¾Š² + + + + Invalid parameter '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ '%s' + + + + Invalid option -%c + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š°Ń Š¾ŠæцŠøя -%c + + + + Invalid option case %i + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š²Š°Ń€ŠøŠ°Š½Ń‚ Š¾ŠæцŠøŠø %i + + + + Unknown option: %s + ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Š°Ń Š¾ŠæцŠøя: %s + + + + Invalid argument format: %s + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ фŠ¾Ń€Š¼Š°Ń‚ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚Š°: %s + + + + Missing the %u%s value for option %s + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ %u%s Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ Š“Š»Ń Š¾ŠæцŠøŠø %s + + + + st + Š¾Šµ + + + + nd + Š¾Šµ + + + + rd + ьŠµ + + + + th + Š¾Šµ + + + Usage: + + + Š˜ŃŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š½ŠøŠµ: + + + + + +Syntax error: %N + + +Š”ŠøŠ½Ń‚Š°ŠŗсŠøчŠµŃŠŗŠ°Ń Š¾ŃˆŠøŠ±ŠŗŠ°: %N + + + + + HostOnly + + + Failed to create the host-only adapter + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Š·Š“Š°Ń‚ŃŒ Š²ŠøртуŠ°Š»ŃŒŠ½Ń‹Š¹ Š°Š“Š°ŠæтŠµŃ€ хŠ¾ŃŃ‚Š° + + + + Interface '%ls' was successfully created + + Š˜Š½Ń‚ŠµŃ€Ń„ŠµŠ¹Ń '%ls' усŠæŠµŃˆŠ½Š¾ сŠ¾Š·Š“Š°Š½ + + + + + + Only one interface name can be specified + ŠœŠ¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ уŠŗŠ°Š·Š°Š½Š¾ тŠ¾Š»ŃŒŠŗŠ¾ Š¾Š“Š½Š¾ ŠøŠ¼Ń ŠøŠ½Ń‚ŠµŃ€Ń„ŠµŠ¹ŃŠ° + + + + No interface name was specified + ŠŠµ уŠŗŠ°Š·Š°Š½Š¾ ŠøŠ¼Ń ŠøŠ½Ń‚ŠµŃ€Ń„ŠµŠ¹ŃŠ° + + + + Failed to remove the host-only adapter + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ уŠ“Š°Š»Šøть Š²ŠøртуŠ°Š»ŃŒŠ½Ń‹Š¹ Š°Š“Š°ŠæтŠµŃ€ хŠ¾ŃŃ‚Š° + + + + The --ip option is specified more than once + ŠžŠæцŠøя --ip уŠŗŠ°Š·Š°Š½Š° Š±Š¾Š»ŠµŠµ Š¾Š“Š½Š¾Š³Š¾ рŠ°Š·Š° + + + + The --netmask option is specified more than once + ŠžŠæцŠøя --netmask уŠŗŠ°Š·Š°Š½Š° Š±Š¾Š»ŠµŠµ Š¾Š“Š½Š¾Š³Š¾ рŠ°Š·Š° + + + + The --ipv6 option is specified more than once + ŠžŠæцŠøя --ipv6 уŠŗŠ°Š·Š°Š½Š° Š±Š¾Š»ŠµŠµ Š¾Š“Š½Š¾Š³Š¾ рŠ°Š·Š° + + + + The --netmasklengthv6 option is specified more than once + ŠžŠæцŠøя --netmasklengthv6 уŠŗŠ°Š·Š°Š½Š° Š±Š¾Š»ŠµŠµ Š¾Š“Š½Š¾Š³Š¾ рŠ°Š·Š° + + + + You can not use --dhcp with static ip configuration parameters: --ip, --netmask, --ipv6 and --netmasklengthv6. + ŠŠµŠ»ŃŒŠ·Ń ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŃŒ --dhcp с ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š°Š¼Šø ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†ŠøŠø стŠ°Ń‚ŠøчŠµŃŠŗŠ¾Š³Š¾ ip: --ip, --netmask, --ipv6 and --netmasklengthv6. + + + + You can not use ipv4 configuration (--ip and --netmask) with ipv6 (--ipv6 and --netmasklengthv6) simultaneously. + ŠŠµŠ»ŃŒŠ·Ń ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŃŒ ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†Šøю ipv4 (--ip Šø --netmask) с ipv6 (--ipv6 Šø --netmasklengthv6) Š¾Š“Š½Š¾Š²Ń€ŠµŠ¼ŠµŠ½Š½Š¾. + + + + Could not find interface '%s' + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š½Š°Š¹Ń‚Šø ŠøŠ½Ń‚ŠµŃ€Ń„ŠµŠ¹Ń '%s' + + + + IPv6 setting is not supported for this adapter + ŠŠ°Š¹ŃŃ‚Ń€Š¾Š¹ŠŗŠø IPv6 Š½Šµ ŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŃŽŃ‚ся Š“Š»Ń этŠ¾Š³Š¾ Š°Š“Š°ŠæтŠµŃ€Š° + + + + Neither -dhcp nor -ip nor -ipv6 was specfified + ŠŠµ уŠŗŠ°Š·Š°Š½Ń‹ Š½Šø -dhcp Š½Šø -ip Š½Šø -ipv6 + + + + + No sub-command specified + ŠŠµ уŠŗŠ°Š·Š°Š½Š° ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“Š° + + + + + Unknown sub-command '%s' + ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Š°Ń ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“Š°: '%s' + + + + The --name parameter must be specified + ŠŸŠ°Ń€Š°Š¼ŠµŃ‚Ń€ --name Š“Š¾Š»Š¶ŠµŠ½ Š±Ń‹Ń‚ŃŒ уŠŗŠ°Š·Š°Š½ + + + + The --netmask parameter must be specified + ŠŸŠ°Ń€Š°Š¼ŠµŃ‚Ń€ --netmask Š“Š¾Š»Š¶ŠµŠ½ Š±Ń‹Ń‚ŃŒ уŠŗŠ°Š·Š°Š½ + + + + The --lower-ip parameter must be specified + ŠŸŠ°Ń€Š°Š¼ŠµŃ‚Ń€ --lower-ip Š“Š¾Š»Š¶ŠµŠ½ Š±Ń‹Ń‚ŃŒ уŠŗŠ°Š·Š°Š½ + + + + The --upper-ip parameter must be specified + ŠŸŠ°Ń€Š°Š¼ŠµŃ‚Ń€ --upper-ip Š“Š¾Š»Š¶ŠµŠ½ Š±Ń‹Ń‚ŃŒ уŠŗŠ°Š·Š°Š½ + + + + + Either --name or --id parameter must be specified + ŠŠµŠ¾Š±Ń…Š¾Š“ŠøŠ¼Š¾ уŠŗŠ°Š·Š°Ń‚ŃŒ Š»ŠøŠ±Š¾ --name Š»ŠøŠ±Š¾ --id + + + + Info + + + %sName: %ls (UUID: %s)%s + + %sŠ˜Š¼Ń: %ls (UUID: %s)%s + + + + + %sDescription: +%ls + + %sŠžŠæŠøсŠ°Š½ŠøŠµ: +%ls + + + + + powered off + Š²Ń‹ŠŗŠ»ŃŽŃ‡ŠµŠ½Š° + + + + saved + сŠ¾Ń…Ń€Š°Š½ŠµŠ½Š° + + + + teleported + ŠæŠ¾Ń€Ń‚ŠøрŠ¾Š²Š°Š½Š° + + + + aborted + ŠæрŠµŃ€Š²Š°Š½Š° + + + + aborted-saved + ŠæрŠµŃ€Š²Š°Š½Š°-сŠ¾Ń…Ń€Š°Š½ŠµŠ½Š° + + + + running + рŠ°Š±Š¾Ń‚Š°ŠµŃ‚ + + + + + paused + ŠæрŠøŠ¾ŃŃ‚Š°Š½Š¾Š²Š»ŠµŠ½Š° + + + + guru meditation + Š³ŃƒŃ€Ńƒ Š¼ŠµŠ“ŠøтŠ°Ń†Šøя + + + + teleporting + ŠæŠ¾Ń€Ń‚ŠøрŠ¾Š²Š°Š½ŠøŠµ + + + + live snapshotting + сŠ¾Š·Š“Š°Š½ŠøŠµ Š¶ŠøŠ²Š¾Š³Š¾ сŠ½ŠøŠ¼ŠŗŠ° + + + + starting + Š·Š°ŠæусŠŗ + + + + stopping + Š¾ŃŃ‚Š°Š½Š¾Š²ŠŗŠ° + + + + saving + сŠ¾Ń…Ń€Š°Š½ŠµŠ½ŠøŠµ + + + + restoring + Š²Š¾ŃŃŃ‚Š°Š½Š¾Š²Š»ŠµŠ½ŠøŠµ + + + + teleporting paused vm + ŠæŠ¾Ń€Ń‚ŠøрŠ¾Š²Š°Š½ŠøŠµ ŠæрŠøŠ¾ŃŃ‚Š°Š½Š¾Š²Š»ŠµŠ½Š½Š¾Š¹ Š²Š¼ + + + + teleporting (incoming) + ŠæŠ¾Ń€Ń‚ŠøрŠ¾Š²Š°Š½ŠøŠµ (ŠøŠ·Š²Š½Šµ) + + + + deleting snapshot live + уŠ“Š°Š»ŠµŠ½ŠøŠµ Š¶ŠøŠ²Š¾Š³Š¾ сŠ½ŠøŠ¼ŠŗŠ° + + + + deleting snapshot live paused + уŠ“Š°Š»ŠµŠ½ŠøŠµ Š¶ŠøŠ²Š¾Š³Š¾ сŠ½ŠøŠ¼ŠŗŠ° ŠæрŠøŠ¾ŃŃ‚Š°Š½Š¾Š²Š»ŠµŠ½Š¾ + + + + online snapshotting + сŠ¾Š·Š“Š°Š½ŠøŠµ Š¾Š½Š»Š°Š¹Š½ сŠ½ŠøŠ¼ŠŗŠ° + + + + restoring snapshot + Š²Š¾ŃŃŃ‚Š°Š½Š¾Š²Š»ŠµŠ½ŠøŠµ сŠ½ŠøŠ¼ŠŗŠ° + + + + deleting snapshot + уŠ“Š°Š»ŠµŠ½ŠøŠµ сŠ½ŠøŠ¼ŠŗŠ° + + + + setting up + Š½Š°ŃŃ‚Ń€Š¾Š¹ŠŗŠ° + + + + offline snapshotting + сŠ¾Š·Š“Š°Š½ŠøŠµ Š¾Ń„Š»Š°Š¹Š½ сŠ½ŠøŠ¼ŠŗŠ° + + + + + + + + + + + + unknown + Š½ŠµŠøŠ·Š²ŠµŃŃ‚Š½Š¾ + + + + + + + not active + Š½ŠµŠ°ŠŗтŠøŠ²Š½Š¾ + + + + pre-initializing + ŠæрŠµŠøŠ½ŠøцŠøŠ°Š»ŠøŠ·Š°Ń†Šøя + + + + initializing + ŠøŠ½ŠøцŠøŠ°Š»ŠøŠ·Š°Ń†Šøя + + + + active/running + Š°ŠŗтŠøŠ²Š½Š¾/рŠ°Š±Š¾Ń‚Š°ŠµŃ‚ + + + + terminating + Š·Š°Š²ŠµŃ€ŃˆŠµŠ½ŠøŠµ + + + + terminated + Š·Š°Š²ŠµŃ€ŃˆŠµŠ½Š¾ + + + + failed + Š¾Ń‚ŠŗŠ°Š· + + + + + + Null + ŠŸŃƒŃŃ‚Š¾ + + + + Disk + Š”ŠøсŠŗ + + + + + Network + Š”ŠµŃ‚ŃŒ + + + Name: '%ls', Type: %s, Limit: none (disabled) + + Š˜Š¼Ń: '%ls', Š¢ŠøŠæ: %s, Š›ŠøŠ¼Šøт: Š½ŠµŃ‚(Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾) + + + + Name: '%ls', Type: %s, Limit: %lld %sbits/sec (%lld %sbytes/sec) + + Š˜Š¼Ń: '%ls', Š¢ŠøŠæ: %s, Š›ŠøŠ¼Šøт: %lld %sŠ±Šøт/сŠµŠŗ (%lld %sŠ±Š°Š¹Ń‚/сŠµŠŗ) + + + + Name: '%ls', Type: %s, Limit: %lld %sbytes/sec + + Š˜Š¼Ń: '%ls', Š¢ŠøŠæ: %s, Š›ŠøŠ¼Šøт: %lld %sŠ±Š°Š¹Ń‚/сŠµŠŗ + + + + + Name: '%ls', Host path: '%ls' (%s), %s%s + Š˜Š¼Ń: '%ls', ŠŸŃƒŃ‚ŃŒ хŠ¾ŃŃ‚Š°: '%ls' (%s), %s%s + + + + writable + Š·Š°ŠæŠøсыŠ²Š°ŠµŠ¼Ń‹Š¹ + + + + readonly + тŠ¾Š»ŃŒŠŗŠ¾ Š“Š»Ń чтŠµŠ½Šøя + + + + , auto-mount + , Š°Š²Ń‚Š¾Š¼Š¾Š½Ń‚ŠøрŠ¾Š²Š°Š½ŠøŠµ + + + + , mount-point: '%ls' + + , тŠ¾Ń‡ŠŗŠ° ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½Šøя: '%ls' + + + + + + + + None + ŠŠµŃ‚ + + + + Automatic + ŠŠ²Ń‚Š¾Š¼Š°Ń‚ŠøчŠµŃŠŗŠø + + + + + + + + + + + + + Unknown + ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Š¾ + + + + Default + ŠŸŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю + + + + Legacy + Š£ŃŃ‚Š°Ń€ŠµŠ²ŃˆŠøŠ¹ + + + + Minimal + ŠœŠøŠ½ŠøŠ¼Š°Š»ŃŒŠ½Ń‹Š¹ + + + + + + + + + + enabled + Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾ + + + + + + + + + + + + disabled + Š²Ń‹ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾ + + + + %sDescription: %ls + + %sŠžŠæŠøсŠ°Š½ŠøŠµ: %ls + + + + + + + + <none> + <Š½ŠµŃ‚> + + + Port %u, Unit %u: UUID: %ls%s%s%s + Location: "%ls" + + ŠŸŠ¾Ń€Ń‚ %u, Š£ŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š¾ %u: UUID: %ls%s%s%s + Š Š°ŃŠæŠ¾Š»Š¾Š¶ŠµŠ½ŠøŠµ: "%ls" + + + + + , passthrough enabled + , ŠæряŠ¼Š¾Š¹ Š“Š¾ŃŃ‚ŃƒŠæ Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½ + + + + + , temp eject + , Š²Ń€ŠµŠ¼ŠµŠ½Š½Š¾ ŠøŠ·Š²Š»ŠµŃ‡ŃŒ + + + + + , ejected + , ŠøŠ·Š²Š»ŠµŃ‡ŠµŠ½Š¾ + + + + , hot-pluggable + + + + + , non-rotational (SSD) + + + + + , discards unused blocks + + + + + Port %u, Unit %u: Empty%s%s + + ŠŸŠ¾Ń€Ń‚ %u, Š£ŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š¾ %u: ŠŸŃƒŃŃ‚Š¾Š¹%s%s + + + + + Port %u, Unit %u: GetMedium failed: %Rhrc + + ŠŸŠ¾Ń€Ń‚ %u, Š£ŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š¾ %u: GetMedium Š·Š°Š²ŠµŃ€ŃˆŠøŠ»ŃŃ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹: %Rhrc + + + + + "<inaccessible>" {%s} + + "<Š½ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠ½Š¾>" {%s} + + + + + Name: <inaccessible!> + + Š˜Š¼Ń: <Š½ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠ½Š¾!> + + + + + Config file: %ls + + Š¤Š°Š¹Š» ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†ŠøŠø: %ls + + + + + Access error details: + + Š”ŠµŃ‚Š°Š»Šø Š¾ŃˆŠøŠ±ŠŗŠø Š“Š¾ŃŃ‚ŃƒŠæŠ°: + + + + + Name: + Š˜Š¼Ń: + + + + Groups: + Š“Ń€ŃƒŠæŠæы: + + + + + Guest OS: + Š“Š¾ŃŃ‚ŠµŠ²Š°Ń ŠžŠ”: + + + + Config file: + Š¤Š°Š¹Š» ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†ŠøŠø: + + + + Snapshot folder: + ŠŸŠ°ŠæŠŗŠ° сŠ½ŠøŠ¼ŠŗŠ¾Š²: + + + + Log folder: + ŠŸŠ°ŠæŠŗŠ° Š¶ŃƒŃ€Š½Š°Š»Š¾Š²: + + + + Hardware UUID: + ŠŠæŠæŠ°Ń€Š°Ń‚Š½Ń‹Š¹ UUID: + + + + Memory size: + Š Š°Š·Š¼ŠµŃ€ ŠæŠ°Š¼ŃŃ‚Šø: + + + + Page Fusion: + Page Fusion: + + + + VRAM size: + Š Š°Š·Š¼ŠµŃ€ VRAM: + + + + CPU exec cap: + ŠŸŃ€Š¾Ń†ŠµŠ½Ń‚ Š²Ń‹ŠæŠ¾Š»Š½ŠµŠ½Šøя Š¦ŠŸŠ£: + + + + HPET: + HPET: + + + + CPUProfile: + ŠŸŃ€Š¾Ń„ŠøŠ»ŃŒ Š¦ŠŸŠ£: + + + + invalid + Š½ŠµŠ“ŠµŠ¹ŃŃ‚Š²ŠøтŠµŠ»ŃŒŠ½Ń‹Š¹ + + + + Chipset: + Š§ŠøŠæсŠµŃ‚: + + + + Firmware: + ŠŸŃ€Š¾ŃˆŠøŠ²ŠŗŠ°: + + + + Number of CPUs: + Š§ŠøсŠ»Š¾ Š¦ŠŸŠ£: + + + + Long Mode: + Š”Š»ŠøŠ½Š½Ń‹Š¹ рŠµŠ¶ŠøŠ¼: + + + + Triple Fault Reset: + Š”Š±Ń€Š¾Ń Š¢Ń€Š¾Š¹Š½Š¾Š³Š¾ ŠžŃ‚ŠŗŠ°Š·Š°: + + + + Nested VT-x/AMD-V: + Š’Š»Š¾Š¶ŠµŠ½Š½Ń‹Šµ Nested VT-x/AMD-V: + + + + CPUID Portability Level: + Š£Ń€Š¾Š²ŠµŠ½ŃŒ ŠŸŠ¾Ń€Ń‚Š°Ń‚ŠøŠ²Š½Š¾ŃŃ‚Šø CPUID: + + + + CPUID overrides: + CPUID Š·Š°Š¼ŠµŠ½Ń‹: + + + + Leaf no. EAX EBX ECX EDX + + Š›Šøст no. EAX EBX ECX EDX + + + + + None + + ŠŠµŃ‚ + + + + + menu only + тŠ¾Š»ŃŒŠŗŠ¾ Š¼ŠµŠ½ŃŽ + + + + message and menu + сŠ¾Š¾Š±Ń‰ŠµŠ½Šøя Šø Š¼ŠµŠ½ŃŽ + + + + Boot menu mode: + Š ŠµŠ¶ŠøŠ¼ Š·Š°Š³Ń€ŃƒŠ·Š¾Ń‡Š½Š¾Š³Š¾ Š¼ŠµŠ½ŃŽ: + + + + Floppy + Š¤Š»Š¾ŠæŠæŠø + + + + HardDisk + Š–ŠµŃŃ‚ŠŗŠøŠ¹ Š”ŠøсŠŗ + + + + Shared Folder + ŠžŠ±Ń‰Š°Ń ŠæŠ°ŠæŠŗŠ° + + + + Not Assigned + ŠŠµ Š½Š°Š·Š½Š°Ń‡ŠµŠ½Š¾ + + + + Boot Device %u: + Š—Š°Š³Ń€ŃƒŠ·Š¾Ń‡Š½Š¾Šµ устрŠ¾Š¹ŃŃ‚Š²Š¾ %u: + + + + BIOS APIC mode: + Š ŠµŠ¶ŠøŠ¼ BIOS APIC: + + + + Time offset: + Š§Š°ŃŠ¾Š²Š¾Š¹ ŠæŠ¾ŃŃ: + + + + ms + Š¼Ń + + + + BIOS NVRAM File: + Š¤Š°Š¹Š» BIOS NVRAM: + + + + RTC: + RTC: + + + + local time + Š»Š¾ŠŗŠ°Š»ŃŒŠ½Š¾Šµ Š²Ń€ŠµŠ¼Ń + + + + Hardware Virtualization: + ŠŠæŠæŠ°Ń€Š°Ń‚Š½Š°Ń Š’ŠøртуŠ°Š»ŠøŠ·Š°Ń†Šøя: + + + + Nested Paging: + Š’Š»Š¾Š¶ŠµŠ½Š½Ń‹Šµ стрŠ°Š½Šøцы: + + + + Large Pages: + Š‘Š¾Š»ŃŒŃˆŠøŠµ Š”трŠ°Š½Šøцы: + + + + VT-x Unrestricted Exec.: + VT-x ŠŠµŠ¾Š³Ń€Š°Š½ŠøчŠµŠ½Š½Š¾Šµ Š²Ń‹ŠæŠ¾Š»Š½ŠµŠ½ŠøŠµ: + + + + AMD-V Virt. Vmsave/Vmload: + AMD-V Virt. Vmsave/Vmload: + + + + Paravirt. Provider: + ŠŸŠ°Ń€Š°Š²ŠøртуŠ°Š»ŃŒŠ½Ń‹Š¹ ŠæрŠ¾Š²Š°Š¹Š“ŠµŃ€: + + + + Effective Paravirt. Prov.: + Š­Ń„Ń„ŠµŠŗтŠøŠ²Š½Ń‹Š¹ ŠŸŠ°Ń€Š°Š²ŠøртуŠ°Š»ŃŒŠ½Ń‹Š¹ ŠæрŠ¾Š²Š°Š¹Š“ŠµŃ€: + + + + Paravirt. Debug: + ŠŸŠ°Ń€Š°Š²ŠøрутŠ°Š»ŃŒŠ½Š°Ń Š¾Ń‚Š»Š°Š“ŠŗŠ°: + + + + %-28s %s (since %s) + + %-28s %s (с %s) + + + + + State: + Š”Š¾ŃŃ‚Š¾ŃŠ½ŠøŠµ: + + + + Graphics Controller: + Š“Ń€Š°Ń„ŠøчŠµŃŠŗŠøŠ¹ ŠšŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€: + + + + Monitor count: + Š§ŠøсŠ»Š¾ Š¼Š¾Š½ŠøтŠ¾Ń€Š¾Š²: + + + + 3D Acceleration: + 3D Š£ŃŠŗŠ¾Ń€ŠµŠ½ŠøŠµ: + + + + 2D Video Acceleration: + 2D Š’ŠøŠ“ŠµŠ¾ Š£ŃŠŗŠ¾Ń€ŠµŠ½ŠøŠµ: + + + + Teleporter Enabled: + Š¢ŠµŠ»ŠµŠæŠ¾Ń€Ń‚ŠµŃ€ Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½: + + + + Teleporter Port: + ŠŸŠ¾Ń€Ń‚ тŠµŠ»ŠµŠæŠ¾Ń€Ń‚ŠµŃ€Š°: + + + + Teleporter Address: + ŠŠ“рŠµŃ тŠµŠ»ŠµŠæŠ¾Ń€Ń‚ŠµŃ€Š°: + + + + Teleporter Password: + ŠŸŠ°Ń€Š¾Š»ŃŒ тŠµŠ»ŠµŠæŠ¾Ń€Ń‚ŠµŃ€Š°: + + + + Tracing Enabled: + Š¢Ń€Š°ŃŃŠøрŠ¾Š²ŠŗŠ° Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½Š°: + + + + Allow Tracing to Access VM: + Š Š°Š·Ń€ŠµŃˆŠøть трŠ°ŃŃŠøрŠ¾Š²ŠŗŠµ Š“Š¾ŃŃ‚ŃƒŠæ Šŗ Š’Šœ: + + + + Tracing Configuration: + ŠšŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†Šøя трŠ°ŃŃŠøрŠ¾Š²ŠŗŠø: + + + + Autostart Enabled: + ŠŠ²Ń‚Š¾ŃŃ‚Š°Ń€Ń‚ Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½: + + + + Autostart Delay: + Š—Š°Š“ŠµŃ€Š¶ŠŗŠ° Š°Š²Ń‚Š¾ŃŃ‚Š°Ń€Ń‚Š°: + + + + Default Frontend: + Š¤Ń€Š¾Š½Ń‚эŠ½Š“ ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю: + + + + flat + рŠ°Š²Š½Š¾Š¼ŠµŃ€Š½Ń‹Š¹ + + + + low + Š½ŠøŠ·ŠŗŠøŠ¹ + + + + normal + Š½Š¾Ń€Š¼Š°Š»ŃŒŠ½Ń‹Š¹ + + + + high + Š²Ń‹ŃŠ¾ŠŗŠøŠ¹ + + + + default + ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю + + + + VM process priority: + ŠŸŃ€ŠøŠ¾Ń€ŠøтŠµŃ‚ ŠæрŠ¾Ń†ŠµŃŃŠ° Š’Šœ: + + + + VMMDev Testing + Š¢ŠµŃŃ‚ŠøрŠ¾Š²Š°Š½ŠøŠµ VMMDev + + + + + misconfigured + Š½ŠµŠæрŠ°Š²ŠøŠ»ŃŒŠ½Š¾ Š½Š°ŃŃ‚Ń€Š¾ŠµŠ½ + + + + * Snapshots: + + * Š”Š½ŠøŠ¼ŠŗŠø: + + + + + * Guest: + + * Š“Š¾ŃŃ‚ŠµŠ²Š°Ń сŠøстŠµŠ¼Š°: + + + + + + Guest Facilities: + Š”рŠµŠ“стŠ²Š° Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ сŠøстŠµŠ¼Ń‹: + + + + + + + on + Š²ŠŗŠ» + + + + + + + off + Š²Ń‹ŠŗŠ» + + + + NIC %u: + NIC %u: + + + + #%zu: Name: '%ls', Type: %s, Limit: none (disabled) + + + + + + #%zu: Name: '%ls', Type: %s, Limit: %RI64 %s (%RI64 %s) + + + + + + #%zu: Name: '%ls', Type: %s, Limit: %RI64 %s + + + + + + Port %u, Unit %u: UUID: %ls%s%s%s%s%s%s + Location: "%ls" + + + + + + + + + + %-28s disabled + + %-28s Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½ + + + + + + + none + Š½ŠµŃ‚ + + + + %sNIC %d Rule(%d): name = %s, protocol = %s, host ip = %s, host port = %s, guest ip = %s, guest port = %s + + %sNIC %d ŠŸŃ€Š°Š²ŠøŠ»Š¾(%d): ŠøŠ¼Ń = %s, ŠæрŠ¾Ń‚Š¾ŠŗŠ¾Š» = %s, хŠ¾ŃŃ‚ ip = %s, хŠ¾ŃŃ‚ ŠæŠ¾Ń€Ń‚ = %s, Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ip = %s, Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠæŠ¾Ń€Ń‚ = %s + + + + + NIC %d Settings: MTU: %d, Socket (send: %d, receive: %d), TCP Window (send:%d, receive: %d) + + NIC %d ŠŠ°ŃŃ‚Ń€Š¾Š¹ŠŗŠø: MTU: %d, Š”Š¾ŠŗŠµŃ‚ (Š¾Ń‚ŠæрŠ°Š²Š»ŠµŠ½Š¾: %d, ŠæрŠøŠ½ŃŃ‚Š¾: %d), ŠžŠŗŠ½Š¾ TCP (Š¾Ń‚ŠæрŠ°Š²Š»ŠµŠ½Š¾:%d, ŠæрŠøŠ½ŃŃ‚Š¾: %d) + + + + + Bridged Interface '%ls' + Š˜Š½Ń‚ŠµŃ€Ń„ŠµŠ¹Ń Š”ŠµŃ‚ŠµŠ²Š¾Š³Š¾ ŠœŠ¾ŃŃ‚Š° '%ls' + + + + Internal Network '%s' + Š’Š½ŃƒŃ‚Ń€ŠµŠ½Š½ŃŃ Š”ŠµŃ‚ŃŒ '%s' + + + + Host-only Interface '%ls' + Š˜Š½Ń‚ŠµŃ€Ń„ŠµŠ¹Ń Š’ŠøртуŠ°Š»ŃŒŠ½Š¾Š¹ Š”ŠµŃ‚Šø '%s' + + + + Generic '%ls' + ŠžŠ±Ń‰ŠøŠ¹ '%ls' + + + + NAT Network '%s' + Š”ŠµŃ‚ŃŒ NAT '%s' + + + + Host Only Network '%s' + Š’ŠøртуŠ°Š»ŃŒŠ½Š°Ń Š”ŠµŃ‚ŃŒ '%s' + + + + Cloud Network '%s' + ŠžŠ±Š»Š°Ń‡Š½Š°Ń Š”ŠµŃ‚ŃŒ '%s' + + + + deny + Š·Š°ŠæрŠµŃ‚Šøть + + + + allow-vms + рŠ°Š·Ń€ŠµŃˆŠøть Š²Š¼ + + + + allow-all + рŠ°Š·Ń€ŠµŃˆŠøть Š²ŃŠµŠ¼ + + + + %-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 + + %-28s MAC: %ls, ŠŸŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ŠøŠµ: %s, ŠšŠ°Š±ŠµŠ»ŃŒ ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½: %s, Š¢Ń€Š°ŃŃŠøрŠ¾Š²ŠŗŠ°: %s (фŠ°Š¹Š»: %ls), Š¢ŠøŠæ: %s, Š—Š°ŃŠ²Š»ŠµŠ½Š½Š°Ń Š”ŠŗŠ¾Ń€Š¾ŃŃ‚ŃŒ: %d MŠ±/с, ŠŸŃ€ŠøŠ¾Ń€ŠøтŠµŃ‚ Š·Š°Š³Ń€ŃƒŠ·ŠŗŠø: %d, ŠŸŠ¾Š»ŠøтŠŗŠ° ŠŠµŃ€Š°Š·Š±Š¾Ń€Ń‡ŠøŠ²Š¾ŃŃ‚Šø: %s, Š“Ń€ŃƒŠæŠæŠ° ŠŸŠ¾Š»Š¾ŃŃ‹ ŠŸŃ€Š¾ŠæусŠŗŠ°Š½Šøя: %ls + + + + + PS/2 Mouse + PS/2 ŠœŃ‹ŃˆŃŒ + + + + USB Mouse + USB ŠœŃ‹ŃˆŃŒ + + + + USB Tablet + USB ŠŸŠ»Š°Š½ŃˆŠµŃ‚ + + + + USB Tablet and PS/2 Mouse + USB ŠŸŠ»Š°Š½ŃˆŠµŃ‚ Šø PS/2 ŠœŃ‹ŃˆŃŒ + + + + USB Multi-Touch + USB ŠœŃƒŠ»ŃŒŃ‚ŠøтŠ°Ń‡ + + + + Pointing Device: + Š£ŠŗŠ°Š·Š°Ń‚ŠµŠ»ŃŒŠ½Š¾Šµ Š£ŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š¾: + + + + PS/2 Keyboard + PS/2 ŠšŠ»Š°Š²ŠøŠ°Ń‚ŃƒŃ€Š° + + + + USB Keyboard + USB ŠšŠ»Š°Š²ŠøŠ°Ń‚ŃƒŃ€Š° + + + + USB and PS/2 Keyboard + USB Šø PS/2 ŠšŠ»Š°Š²ŠøŠ°Ń‚ŃƒŃ€Š° + + + + Keyboard Device: + ŠšŠ»Š°Š²ŠøŠ°Ń‚ŃƒŃ€Š°: + + + + UART %u: + UART %u: + + + + + %-28s I/O base: %#06x, IRQ: %d + %-28s Š‘Š°Š·Š° I/O: %#06x, IRQ: %d + + + + , disconnected + , Š¾Ń‚сŠ¾ŠµŠ“ŠøŠ½ŠµŠ½ + + + + , attached to raw file '%ls' + + , ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ Šŗ raw фŠ°Š¹Š»Ńƒ '%ls' + + + + + , attached to tcp (%s) '%ls' + , ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ Šŗ tcp (%s) '%ls' + + + + + server + сŠµŃ€Š²ŠµŃ€ + + + + + client + ŠŗŠ»ŠøŠµŠ½Ń‚ + + + + , attached to pipe (%s) '%ls' + , ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ Šŗ pipe (%s) '%ls' + + + + , attached to device '%ls' + , ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ Šŗ устрŠ¾Š¹ŃŃ‚Š²Ńƒ '%ls' + + + + LPT %u: + LPT %u: + + + + , attached to device '%ls' + + , ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ Šŗ устрŠ¾Š¹ŃŃ‚Š²Ńƒ '%ls' + + + + + Audio: + ŠŃƒŠ“ŠøŠ¾: + + + + (Driver: %s, Controller: %s, Codec: %s) + (Š”Ń€Š°Š¹Š²ŠµŃ€: %s, ŠšŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€: %s, ŠšŠ¾Š“ŠµŠŗ: %s) + + + + Audio playback: + ŠŃƒŠ“ŠøŠ¾ Š²Š¾ŃŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½ŠøŠµ: + + + + Audio capture: + ŠŃƒŠ“ŠøŠ¾ Š·Š°Ń…Š²Š°Ń‚: + + + + + HostToGuest + Š„Š¾ŃŃ‚->Š“Š¾ŃŃ‚ŃŒ + + + + + GuestToHost + Š“Š¾ŃŃ‚ŃŒ->Š„Š¾ŃŃ‚ + + + + + Bidirectional + Š”Š²ŃƒŠ½Š°ŠæрŠ°Š²Š»ŠµŠ½Š½Ń‹Š¹ + + + + Clipboard Mode: + Š ŠµŠ¶ŠøŠ¼ Š±ŃƒŃ„ŠµŃ€Š° Š¾Š±Š¼ŠµŠ½Š°: + + + + Clipboard file transfers: + ŠŸŠµŃ€ŠµŠ“Š°Ń‡Š° фŠ°Š¹Š»Š¾Š² чŠµŃ€ŠµŠ· Š±ŃƒŃ„ŠµŃ€ Š¾Š±Š¼ŠµŠ½Š°: + + + + Drag and drop Mode: + Š ŠµŠ¶ŠøŠ¼ Drag and Drop: + + + + Session name: + Š˜Š¼Ń сŠµŃŃŠøŠø: + + + + unknown status + Š½ŠµŠøŠ·Š²ŠµŃŃ‚Š½Ń‹Š¹ стŠ°Ń‚ŃƒŃ + + + + blank + ŠæустŠ¾Š¹ + + + + Video mode: + Š’ŠøŠ“ŠµŠ¾ рŠµŠ¶ŠøŠ¼: + + + + null + ŠæустŠ¾ + + + + external + Š²Š½ŠµŃˆŠ½ŠøŠ¹ + + + + guest + Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ + + + + %-28s enabled (Address %ls, Ports %ls, MultiConn: %s, ReuseSingleConn: %s, Authentication type: %s) + + %-28s Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½ (ŠŠ“рŠµŃ %ls, ŠŸŠ¾Ń€Ń‚Ń‹ %ls, ŠœŠ½Š¾Š³Š¾ Š”Š¾ŠµŠ“ŠøŠ½ŠµŠ½ŠøŠ¹: %s, ŠžŠ“Š½Š¾ Š”Š¾ŠµŠ“ŠøŠ½ŠµŠ½ŠøŠµ : %s, Š¢ŠøŠæ ŠŃƒŃ‚ŠµŠ½Ń‚ŠøфŠøŠŗŠ°Ń†ŠøŠø: %s) + + + + + VRDE port: + ŠŸŠ¾Ń€Ń‚ VRDE: + + + + %-28s enabled (Quality %ls) + + %-28s Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½ (ŠšŠ°Ń‡ŠµŃŃ‚Š²Š¾ %ls) + + + + + + Video redirection: + Š’ŠøŠ“ŠµŠ¾ ŠæŠµŃ€ŠµŠ½Š°ŠæрŠ°Š²Š»ŠµŠ½ŠøŠµ: + + + + %-28s: %-10lS = <not set> + + %-28s: %-10lS = <Š½Šµ Š·Š°Š“Š°Š½> + + + + + + VRDE property + Š”Š²Š¾Š¹ŃŃ‚Š²Š¾ VRDE + + + + Index: + Š˜Š½Š“ŠµŠŗс: + + + + yes + Š“Š° + + + + no + Š½ŠµŃ‚ + + + + VendorId: + ID ŠæŠ¾ŃŃ‚Š°Š²Ń‰ŠøŠŗŠ°: + + + + ProductId: + ID ŠæрŠ¾Š“уŠŗтŠ°: + + + + Revision: + Š ŠµŠ²ŠøŠ·Šøя: + + + + Manufacturer: + ŠŸŃ€Š¾ŠøŠ·Š²Š¾Š“ŠøтŠµŠ»ŃŒ: + + + + Product: + ŠŸŃ€Š¾Š“уŠŗт: + + + + Masked Interfaces: + Š”ŠŗрытыŠµ Š˜Š½Ń‚ŠµŃ€Ń„ŠµŠ¹ŃŃ‹: + + + + SerialNumber: + Š”ŠµŃ€ŠøŠ¹Š½Ń‹Š¹ ŠŠ¾Š¼ŠµŃ€: + + + + Address: + ŠŠ“рŠµŃ: + + + + +Attached physical PCI devices: + + + +ŠŸŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½Š½Ń‹Šµ фŠøŠ·ŠøчŠµŃŠŗŠøŠµ PCI устрŠ¾Š¹ŃŃ‚Š²Š°: + + + + + + Host device %ls at %s attached as %s + + Š„Š¾ŃŃ‚ устрŠ¾Š¹ŃŃ‚Š²Š¾ %ls Š² %s, ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½Š½Š¾Šµ ŠŗŠ°Šŗ %s + + + + + Shared folders: + ŠžŠ±Ń‰ŠøŠµ ŠŸŠ°ŠæŠŗŠø: + + + + global mapping + Š³Š»Š¾Š±Š°Š»ŃŒŠ½Š¾Šµ Š¾Ń‚Š¾Š±Ń€Š°Š¶ŠµŠ½ŠøŠµ + + + + machine mapping + Š¼Š°ŃˆŠøŠ½Š½Š¾Šµ Š¾Ń‚Š¾Š±Ń€Š°Š¶ŠµŠ½ŠøŠµ + + + + transient mapping + Š²Ń€ŠµŠ¼ŠµŠ½Š½Š¾Šµ Š¾Ń‚Š¾Š±Ń€Š°Š¶ŠµŠ½ŠøŠµ + + + + + <none> + + <Š½ŠµŃ‚> + + + + + + Storage Controllers: + ŠšŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€Ń‹ Š½Š¾ŃŠøтŠµŠ»ŠµŠ¹: + + + + #%u: '%ls', Type: %s, Instance: %u, Ports: %u (max %u), %s + + #%u: '%ls', Š¢ŠøŠæ: %s, Š­ŠŗŠ·ŠµŠ¼ŠæŠ»ŃŃ€: %u, ŠŸŠ¾Ń€Ń‚Ń‹: %u (Š¼Š°Šŗс. %u), %s + + + + + Bootable + Š—Š°Š³Ń€ŃƒŠ·Š¾Ń‡Š½Ń‹Š¹ + + + + Not bootable + ŠŠµŠ·Š°Š³Ń€ŃƒŠ·Š¾Ń‡Š½Ń‹Š¹ + + + + USB Device Filters: + + Š¤ŠøŠ»ŃŒŃ‚ры USB устрŠ¾Š¹ŃŃ‚Š²: + + + + + Active: + ŠŠŗтŠøŠ²Š½Š¾: + + + + Name: + Š˜Š¼Ń: + + + + VendorId: + ID ŠæŠ¾ŃŃ‚Š°Š²Ń‰ŠøŠŗŠ°: + + + + ProductId: + ID ŠæрŠ¾Š“уŠŗтŠ°: + + + + Revision: + Š ŠµŠ²ŠøŠ·Šøя: + + + + Manufacturer: + ŠŸŃ€Š¾ŠøŠ·Š²Š¾Š“ŠøтŠµŠ»ŃŒ: + + + + Product: + ŠŸŃ€Š¾Š“уŠŗт: + + + + Remote: + Š£Š“Š°Š»ŠµŠ½Š½Š¾Šµ: + + + + Serial Number: + Š”ŠµŃ€ŠøŠ¹Š½Ń‹Š¹ Š½Š¾Š¼ŠµŃ€: + + + + USB Device Filters: + Š¤ŠøŠ»ŃŒŃ‚ры USB устрŠ¾Š¹ŃŃ‚Š²: + + + + Available remote USB devices: + Š”Š¾ŃŃ‚ŃƒŠæŠ½Ń‹Šµ уŠ“Š°Š»ŠµŠ½Š½Ń‹Šµ USB устрŠ¾Š¹ŃŃ‚Š²Š°: + + + + Currently attached USB devices: + Š”ŠµŠ¹Ń‡Š°Ń ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½Š½Ń‹Šµ USB устрŠ¾Š¹ŃŃ‚Š²Š°: + + + + Bandwidth groups: + Š“Ń€ŃƒŠæŠæы ŠæŠ¾Š»Š¾ŃŃ‹ ŠæрŠ¾ŠæусŠŗŠ°Š½Šøя: + + + + VRDE Connection: + VRDE Š”Š¾ŠµŠ“ŠøŠ½ŠµŠ½ŠøŠµ: + + + + + + active + Š°ŠŗтŠøŠ²Š½Š¾Šµ + + + + Clients so far: + ŠšŠ»ŠøŠµŠ½Ń‚Ń‹ Š½Š° Š“Š°Š½Š½Ń‹Š¹ Š¼Š¾Š¼ŠµŠ½Ń‚: + + + + Start time: + Š’Ń€ŠµŠ¼Ń стŠ°Ń€Ń‚Š°: + + + + Last started: + ŠŸŠ¾ŃŠ»ŠµŠ“Š½ŠøŠ¹ рŠ°Š· Š½Š°Ń‡Š°Ń‚Š¾: + + + + Last ended: + ŠŸŠ¾ŃŠ»ŠµŠ“Š½ŠøŠ¹ рŠ°Š· Š·Š°ŠŗŠ¾Š½Ń‡ŠµŠ½Š¾: + + + + Sent: + ŠžŃ‚ŠæрŠ°Š²Š»ŠµŠ½Š¾: + + + + + + + Bytes + Š‘Š°Š¹Ń‚ + + + + Average speed: + Š”рŠµŠ“Š½ŃŃ сŠŗŠ¾Ń€Š¾ŃŃ‚ŃŒ: + + + + + B/s + Š‘/с + + + + Sent total: + Š’сŠµŠ³Š¾ Š¾Ń‚ŠæрŠ°Š²Š»ŠµŠ½Š¾: + + + + Received: + ŠŸŠ¾Š»ŃƒŃ‡ŠµŠ½Š¾: + + + + Speed: + Š”ŠŗŠ¾Ń€Š¾ŃŃ‚ŃŒ: + + + + Received total: + Š’сŠµŠ³Š¾ ŠæŠ¾Š»ŃƒŃ‡ŠµŠ½Š¾: + + + + User name: + Š˜Š¼Ń ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Ń: + + + + Domain: + Š”Š¾Š¼ŠµŠ½: + + + + Client name: + Š˜Š¼Ń ŠŗŠ»ŠøŠµŠ½Ń‚Š°: + + + + Client IP: + IP ŠŗŠ»ŠøŠµŠ½Ń‚Š°: + + + + Client version: + Š’ŠµŃ€ŃŠøя ŠŗŠ»ŠøŠµŠ½Ń‚Š°: + + + + Encryption: + ŠØŠøфрŠ¾Š²Š°Š½ŠøŠµ: + + + + Capturing: + Š—Š°Ń…Š²Š°Ń‚: + + + + Capture audio: + Š—Š°Ń…Š²Š°Ń‚ Š°ŃƒŠ“ŠøŠ¾: + + + + Capture screens: + Š—Š°Ń…Š²Š°Ń‚ эŠŗрŠ°Š½Š¾Š²: + + + + Capture file: + Š¤Š°Š¹Š» Š·Š°Ń…Š²Š°Ń‚Š°: + + + + Capture dimensions: + Š Š°Š·Š¼ŠµŃ€Ń‹ Š·Š°Ń…Š²Š°Ń‚Š°: + + + + Capture rate: + Š‘ŠøтрŠµŠ¹Ń‚ Š·Š°Ń…Š²Š°Ń‚Š°: + + + + + kbps + ŠŗŠ±/с + + + + Capture FPS: + FPS Š·Š°Ń…Š²Š°Ń‚Š°: + + + + Capture options: + ŠžŠæцŠøŠø Š·Š°Ń…Š²Š°Ń‚Š°: + + + + Description: +%ls + + ŠžŠæŠøсŠ°Š½ŠøŠµ +%ls + + + + + MB + ŠœŠ‘ + + + + Configured memory balloon: + ŠŠ°ŃŃ‚Ń€Š¾ŠµŠ½Š½Ń‹Š¹ balloon ŠæŠ°Š¼ŃŃ‚Šø: + + + + OS type: + Š¢ŠøŠæ ŠžŠ”: + + + + Additions run level: + Š£Ń€Š¾Š²ŠµŠ½ŃŒ Š²Ń‹ŠæŠ¾Š»Š½ŠµŠ½Šøя Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½ŠøŠ¹: + + + + Additions version: + Š’ŠµŃ€ŃŠøя Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½ŠøŠ¹: + + + + Facility "%ls": %s (last update: %s) + + Š”рŠµŠ“стŠ²Š¾ "%ls": %s (ŠæŠ¾ŃŠ»ŠµŠ“Š½ŠµŠµ Š¾Š±Š½Š¾Š²Š»ŠµŠ½ŠøŠµ: %s) + + + + + Invalid parameter '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ '%s' + + + + VM name or UUID required + Š¢Ń€ŠµŠ±ŃƒŠµŃ‚ся ŠøŠ¼Ń Š’Šœ ŠøŠ»Šø UUID + + + + Option --log is exclusive + ŠžŠæцŠøя --log эŠŗсŠŗŠ»ŃŽŠ·ŠøŠ²Š½Š°Ń + + + + Internal + + + Usage: VBoxManage internalcommands <command> [command arguments] + +Commands: + +%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%sWARNING: This is a development tool and shall only be used to analyse + problems. It is completely unsupported and will change in + incompatible ways without warning. + + Š˜ŃŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š½ŠøŠµ: VBoxManage internalcommands <ŠŗŠ¾Š¼Š°Š½Š“Š°> [Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚Ń‹ ŠŗŠ¾Š¼Š°Š½Š“ы] + +ŠšŠ¾Š¼Š°Š½Š“ы: + +%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%sŠŸŠ Š•Š”Š£ŠŸŠ Š•Š–Š”Š•ŠŠ˜Š•: Š­Ń‚Š¾ ŠøŠ½ŃŃ‚Ń€ŃƒŠ¼ŠµŠ½Ń‚ рŠ°Š·Ń€Š°Š±Š¾Ń‚чŠøŠŗŠ° Šø Š“Š¾Š»Š¶ŠµŠ½ ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŃŒŃŃ + тŠ¾Š»ŃŒŠŗŠ¾ Š“Š»Ń Š°Š½Š°Š»ŠøŠ·Š° ŠæрŠ¾Š±Š»ŠµŠ¼. ŠžŠ½ Š½Šµ ŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŃ‚ся Š²Š¾Š¾Š±Ń‰Šµ + Šø Š¼Š¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ ŠøŠ·Š¼ŠµŠ½ŠµŠ½ Š±ŠµŠ· ŠæрŠµŠ“уŠæрŠµŠ¶Š“ŠµŠ½Šøя Šø Š±ŠµŠ· сŠ¾Ń…Ń€Š°Š½ŠµŠ½Šøя + сŠ¾Š²Š¼ŠµŃŃ‚ŠøŠ¼Š¾ŃŃ‚Šø. + + + + + loadmap <vmname|uuid> <symfile> <address> [module] [subtrahend] [segment] + This will instruct DBGF to load the given map file + during initialization. (See also loadmap in the debugger.) + + + loadmap <ŠøŠ¼Ń Š²Š¼|uuid> <фŠ°Š¹Š» сŠøŠ¼Š²Š¾Š»Š¾Š²> <Š°Š“рŠµŃ> [module] [subtrahend] [segment] + ŠžŠ½Š° Š³Š¾Š²Š¾Ń€Šøт DBGF Š·Š°Š³Ń€ŃƒŠ·Šøть уŠŗŠ°Š·Š°Š½Š½Ń‹Š¹ map фŠ°Š¹Š» Š²Š¾ Š²Ń€ŠµŠ¼Ń + ŠøŠ½ŠøцŠøŠ°Š»ŠøŠ·Š°Ń†ŠøŠø. (Š”Š¼Š¾Ń‚Ń€Šø loadmap у Š¾Ń‚Š»Š°Š“чŠøŠŗŠ°.) + + + + + + loadsyms <vmname|uuid> <symfile> [delta] [module] [module address] + This will instruct DBGF to load the given symbol file + during initialization. + + + loadmap <ŠøŠ¼Ń Š²Š¼|uuid> <фŠ°Š¹Š» сŠøŠ¼Š²Š¾Š»Š¾Š²> [delta] [module] [module address] + ŠžŠ½Š° Š³Š¾Š²Š¾Ń€Šøт DBGF Š·Š°Š³Ń€ŃƒŠ·Šøть уŠŗŠ°Š·Š°Š½Š½Ń‹Š¹ фŠ°Š¹Š» сŠøŠ¼Š²Š¾Š»Š¾Š² + Š²Š¾ Š²Ń€ŠµŠ¼Ń ŠøŠ½ŠøцŠøŠ°Š»ŠøŠ·Š°Ń†ŠøŠø. + + + + + + sethduuid <filepath> [<uuid>] + Assigns a new UUID to the given image file. This way, multiple copies + of a container can be registered. + + + sethduuid <Šæуть Šŗ фŠ°Š¹Š»Ńƒ> [<uuid>] + ŠŠ°Š·Š½Š°Ń‡Š°ŠµŃ‚ Š½Š¾Š²Ń‹Š¹ UUID Š“Š°Š½Š½Š¾Š¼Ńƒ фŠ°Š¹Š»Ńƒ Š¾Š±Ń€Š°Š·Š°. Š¢Š°ŠŗŠøŠ¼ Š¾Š±Ń€Š°Š·Š¾Š¼, Š¼Š¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ + Š·Š°Ń€ŠµŠ³ŠøстрŠøрŠ¾Š²Š°Š½Š¾ Š½ŠµŃŠŗŠ¾Š»ŃŒŠŗŠ¾ ŠŗŠ¾ŠæŠøŠ¹ Š¾Š“Š½Š¾Š³Š¾ Šø тŠ¾Š³Š¾ Š¶Šµ ŠŗŠ¾Š½Ń‚ŠµŠ¹Š½ŠµŃ€Š°. + + + + + + sethdparentuuid <filepath> <uuid> + Assigns a new parent UUID to the given image file. + + + sethdparentuuid <Šæуть Šŗ фŠ°Š¹Š»Ńƒ> <uuid> + ŠŠ°Š·Š½Š°Ń‡Š°ŠµŃ‚ Š½Š¾Š²Ń‹Š¹ рŠ¾Š“ŠøтŠµŠ»ŃŒŃŠŗŠøŠ¹ UUID Š“Š°Š½Š½Š¾Š¼Ńƒ фŠ°Š¹Š»Ńƒ Š¾Š±Ń€Š°Š·Š°. + + + + + + dumphdinfo <filepath> + Prints information about the image at the given location. + + + dumphdinfo <Šæуть Šŗ фŠ°Š¹Š»Ńƒ> + Š’Ń‹Š²Š¾Š“Šøт ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†Šøю Š¾Š± Š¾Š±Ń€Š°Š·Šµ ŠæŠ¾ уŠŗŠ°Š·Š°Š½Š½Š¾Š¼Ńƒ рŠ°ŃŠæŠ¾Š»Š¾Š¶ŠµŠ½Šøю. + + + + + + listpartitions -rawdisk <diskname> + Lists all partitions on <diskname>. + + + listpartitions -rawdisk <ŠøŠ¼Ń Š“ŠøсŠŗŠ°> + ŠŸŠµŃ€ŠµŃ‡ŠøсŠ»Šøть Š²ŃŠµ рŠ°Š·Š“ŠµŠ»Ń‹ Š½Š° <ŠøŠ¼Ń Š“ŠøсŠŗŠ°>. + + + + + + createrawvmdk -filename <filename> -rawdisk <diskname> + [-partitions <list of partition numbers> [-mbr <filename>] ] + [-relative] + Creates a new VMDK image which gives access to an entire host disk (if + the parameter -partitions is not specified) or some partitions of a + host disk. If access to individual partitions is granted, then the + parameter -mbr can be used to specify an alternative MBR to be used + (the partitioning information in the MBR file is ignored). + The diskname is on Linux e.g. /dev/sda, and on Windows e.g. + \\.\PhysicalDrive0). + On Linux or FreeBSD host the parameter -relative causes a VMDK file to + be created which refers to individual partitions instead to the entire + disk. + The necessary partition numbers can be queried with + VBoxManage internalcommands listpartitions + + + createrawvmdk -filename <ŠøŠ¼Ń фŠ°Š¹Š»Š°> -rawdisk <ŠøŠ¼Ń Š“ŠøсŠŗŠ°> + [-partitions <сŠæŠøсŠ¾Šŗ Š½Š¾Š¼ŠµŃ€Š¾Š² рŠ°Š·Š“ŠµŠ»Š¾Š²> [-mbr <ŠøŠ¼Ń фŠ°Š¹Š»Š°>] ] + [-relative] + Š”Š¾Š·Š“Š°ŠµŃ‚ Š½Š¾Š²Ń‹Š¹ Š¾Š±Ń€Š°Š· VMDK, ŠŗŠ¾Ń‚Š¾Ń€Ń‹Š¹ Š“Š°ŠµŃ‚ Š“Š¾ŃŃ‚ŃƒŠæ ŠŗŠ¾ Š²ŃŠµŠ¼Ńƒ Š“ŠøсŠŗу (ŠµŃŠ»Šø + ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ -partitions Š½Šµ уŠŗŠ°Š·Š°Š½) ŠøŠ»Šø Šŗ Š½ŠµŠŗŠ¾Ń‚Š¾Ń€Ń‹Š¼ рŠ°Š·Š“ŠµŠ»Š°Š¼ Š“ŠøсŠŗŠ° хŠ¾ŃŃ‚Š°. + Š•ŃŠ»Šø Š“Š¾ŃŃ‚ŃƒŠæ Šŗ ŠŗŠ¾Š½ŠŗрŠµŃ‚Š½Ń‹Š¼ рŠ°Š·Š“ŠµŠ»Š°Š¼ рŠ°Š·Ń€ŠµŃˆŠµŠ½, ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ -mbr Š¼Š¾Š¶ŠµŃ‚ + ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŃŒŃŃ Š“Š»Ń уŠŗŠ°Š·Š°Š½Šøя Š°Š»ŃŒŃ‚ŠµŃ€Š½Š°Ń‚ŠøŠ²Š½Š¾Š³Š¾ MBR (ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†Šøя Š¾ рŠ°Š·Š“ŠµŠ»Š°Ń… + Š² MBR фŠ°Š¹Š»Šµ ŠøŠ³Š½Š¾Ń€ŠøруŠµŃ‚ся). Š˜Š¼Ń Š“ŠøсŠŗŠ° Š² Linux - этŠ¾ Š½Š°ŠæрŠøŠ¼ŠµŃ€ /dev/sda, + Š° Š² Windows - Š½Š°ŠæрŠøŠ¼ŠµŃ€ \\.\PhysicalDrive0. + Š’ хŠ¾ŃŃ‚Š°Ń… Linux ŠøŠ»Šø FreeBSD, ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ -relative Š·Š°ŃŃ‚Š°Š²Š»ŃŠµŃ‚ сŠ¾Š·Š“Š°Ń‚ŃŒ фŠ°Š¹Š» + VMDK, ссыŠ»Š°ŃŽŃ‰ŠøŠ¹ŃŃ Š½Š° ŠøŠ½Š“ŠøŠ²ŠøŠ“уŠ°Š»ŃŒŠ½Ń‹Šµ рŠ°Š·Š“ŠµŠ»Ń‹ Š²Š¼ŠµŃŃ‚Š¾ Š²ŃŠµŠ³Š¾ Š“ŠøсŠŗŠ°. + ŠŠµŠ¾Š±Ń…Š¾Š“ŠøŠ¼Ń‹Šµ Š½Š¾Š¼ŠµŃ€Š° рŠ°Š·Š“ŠµŠ»Š¾Š² Š¼Š¾Š³ŃƒŃ‚ Š±Ń‹Ń‚ŃŒ ŠæŠ¾Š»ŃƒŃ‡ŠµŠ½Ń‹ чŠµŃ€ŠµŠ· + VBoxManage internalcommands listpartitions + + + + + + renamevmdk -from <filename> -to <filename> + Renames an existing VMDK image, including the base file and all its extents. + + + renamevmdk -from <ŠøŠ¼Ń фŠ°Š¹Š»Š°> -to <ŠøŠ¼Ń фŠ°Š¹Š»Š°> + ŠŸŠµŃ€ŠµŠ¼ŠµŠ½Š¾Š²Ń‹Š²Š°ŠµŃ‚ сущŠµŃŃ‚Š²ŃƒŃŽŃ‰ŠøŠ¹ VMDK Š¾Š±Ń€Š°Š·, Š²ŠŗŠ»ŃŽŃ‡Š°ŃŃ Š±Š°Š·Š¾Š²Ń‹Š¹ фŠ°Š¹Š» Šø Š²ŃŠµ ŠµŠ³Š¾ эŠŗстŠµŠ½Ń‚Ń‹. + + + + + + converttoraw [-format <fileformat>] <filename> <outputfile>|stdout + Convert image to raw, writing to file or stdout. + + + converttoraw [-format <фŠ¾Ń€Š¼Š°Ń‚ фŠ°Š¹Š»Š°>] <ŠøŠ¼Ń фŠ°Š¹Š»Š°> <Š²Ń‹Ń…Š¾Š“Š½Š¾Š¹ фŠ°Š¹Š»>|stdout + ŠŸŃ€ŠµŠ¾Š±Ń€Š°Š·ŃƒŠµŃ‚ Š¾Š±Ń€Š°Š· Š² raw, Š·Š°ŠæŠøсыŠ²Š°Ń Š² фŠ°Š¹Š» ŠøŠ»Šø stdout. + + + + + + converttoraw [-format <fileformat>] <filename> <outputfile> + Convert image to raw, writing to file. + + + converttoraw [-format <фŠ¾Ń€Š¼Š°Ń‚ фŠ°Š¹Š»Š°>] <ŠøŠ¼Ń фŠ°Š¹Š»Š°> <Š²Ń‹Ń…Š¾Š“Š½Š¾Š¹ фŠ°Š¹Š»> + ŠŸŃ€ŠµŠ¾Š±Ń€Š°Š·ŃƒŠµŃ‚ Š¾Š±Ń€Š°Š· Š² raw, Š·Š°ŠæŠøсыŠ²Š°Ń Š² фŠ°Š¹Š». + + + + + + converthd [-srcformat VDI|VMDK|VHD|RAW] + [-dstformat VDI|VMDK|VHD|RAW] + <inputfile> <outputfile> + converts hard disk images between formats + + + converthd [-srcformat VDI|VMDK|VHD|RAW] + [-dstformat VDI|VMDK|VHD|RAW] + <Š²Ń…Š¾Š“Š½Š¾Š¹ фŠ°Š¹Š»> <Š²Ń‹Ń…Š¾Š“Š½Š¾Š¹ фŠ°Š¹Š»> + ŠæрŠµŠ¾Š±Ń€Š°Š·ŃƒŠµŃ‚ Š¾Š±Ń€Š°Š·Ń‹ Š¶ŠµŃŃ‚ŠŗŠøх Š“ŠøсŠŗŠ¾Š² Š² рŠ°Š·Š½Ń‹Šµ фŠ¾Ń€Š¼Š°Ń‚Ń‹ + + + + + + repairhd [-dry-run] + [-format VDI|VMDK|VHD|...] + <filename> + Tries to repair corrupted disk images + + + repairhd [-dry-run] + [-format VDI|VMDK|VHD|...] + <ŠøŠ¼Ń фŠ°Š¹Š»Š°> + ŠŸŃ‹Ń‚Š°ŠµŃ‚ся Š²Š¾ŃŃŃ‚Š°Š½Š¾Š²Šøть ŠæŠ¾Š²Ń€ŠµŠ¶Š“ŠµŠ½Š½Ń‹Š¹ Š¾Š±Ń€Š°Š· Š“ŠøсŠŗŠ° + + + + + + modinstall + Installs the necessary driver for the host OS + + + modinstall + Š£ŃŃ‚Š°Š½Š°Š²Š»ŠøŠ²Š°ŠµŃ‚ Š½ŃƒŠ¶Š½Ń‹Š¹ Š“рŠ°Š¹Š²ŠµŃ€ Š² ŠžŠ” хŠ¾ŃŃ‚Š° + + + + + + moduninstall + Deinstalls the driver + + + moduninstall + Š£Š“Š°Š»ŃŠµŃ‚ Š“рŠ°Š¹Š²ŠµŃ€ + + + + + + debuglog <vmname|uuid> [--enable|--disable] [--flags todo] + [--groups todo] [--destinations todo] + Controls debug logging. + + + debuglog <ŠøŠ¼Ń Š²Š¼|uuid> [--enable|--disable] [--flags todo] + [--groups todo] [--destinations todo] + ŠšŠ¾Š½Ń‚Ń€Š¾Š»ŠøруŠµŃ‚ Š¾Ń‚Š»Š°Š“Š¾Ń‡Š½Š¾Šµ Š¶ŃƒŃ€Š½Š°Š»ŠøрŠ¾Š²Š°Š½ŠøŠµ. + + + + + + passwordhash <password> + Generates a password hash. + + + passwordhash <ŠæŠ°Ń€Š¾Š»ŃŒ> + Š“ŠµŠ½ŠµŃ€ŠøруŠµŃ‚ хэш ŠæŠ°Ń€Š¾Š»Ń. + + + + + + gueststats <vmname|uuid> [--interval <seconds>] + Obtains and prints internal guest statistics. + Sets the update interval if specified. + + + gueststats <ŠøŠ¼Ń Š²Š¼|uuid> [--interval <сŠµŠŗуŠ½Š“ы>] + ŠŸŠ¾Š»ŃƒŃ‡Š°ŠµŃ‚ Šø Š²Ń‹Š²Š¾Š“Šøт Š²Š½ŃƒŃ‚Ń€ŠµŠ½Š½ŃŽŃŽ стŠ°Ń‚ŠøстŠøŠŗу Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ сŠøстŠµŠ¼Ń‹. + Š—Š°Š“Š°ŠµŃ‚ ŠøŠ½Ń‚ŠµŃ€Š²Š°Š» Š¾Š±Š½Š¾Š²Š»ŠµŠ½Šøя ŠµŃŠ»Šø уŠŗŠ°Š·Š°Š½Š¾. + + + + + + Cannot find unique key for '%s'! + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š½Š°Š¹Ń‚Šø уŠ½ŠøŠŗŠ°Š»ŃŒŠ½Ń‹Š¹ ŠŗŠ»ŃŽŃ‡ Š“Š»Ń '%s'! + + + + Failed to delete key '%s' from '%s', string conversion error %Rrc! + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ уŠ“Š°Š»Šøть ŠŗŠ»ŃŽŃ‡ '%s' ŠøŠ· '%s', Š¾ŃˆŠøŠ±ŠŗŠ° ŠæрŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Š½Šøя стрŠ¾Šŗ %Rrc! + + + + Failed to set '%s/%s/%s' to '%s'! hrc=%#x + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°Š“Š°Ń‚ŃŒ '%s/%s/%s' Š² '%s'! hrc=%#x + + + + + Missing the filename argument! + + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ ŠøŠ¼ŠµŠ½Šø фŠ°Š¹Š»Š°! + + + + + Failed to read delta '%s', rc=%Rrc + + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾Ń‡ŠµŃŃ‚ŃŒ Š“ŠµŠ»ŃŒŃ‚Ńƒ '%s', rc=%Rrc + + + + + + Failed to read module address '%s', rc=%Rrc + + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾Ń‡ŠµŃŃ‚ŃŒ Š°Š“рŠµŃ Š¼Š¾Š“уŠ»Ń '%s', rc=%Rrc + + + + + Failed to read module size '%s', rc=%Rrc + + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾Ń‡ŠµŃŃ‚ŃŒ рŠ°Š·Š¼ŠµŃ€ Š¼Š¾Š“уŠ»Ń '%s', rc=%Rrc + + + + + Missing the module address argument! + + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Š°Š“рŠµŃŠ° Š¼Š¾Š“уŠ»Ń! + + + + + Failed to read subtrahend '%s', rc=%Rrc + + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾Ń‡ŠµŃŃ‚ŃŒ Š²Ń‹Ń‡ŠøтŠ°ŠµŠ¼Š¾Šµ '%s', rc=%Rrc + + + + + Failed to read segment number '%s', rc=%Rrc + + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾Ń‡ŠµŃŃ‚ŃŒ Š½Š¾Š¼ŠµŃ€ сŠµŠ³Š¼ŠµŠ½Ń‚Š° '%s', rc=%Rrc + + + + + Error code %Rrc at %s(%u) in function %s + ŠšŠ¾Š“ Š¾ŃˆŠøŠ±ŠŗŠø %Rrc Š² %s(%u) Š² фуŠ½ŠŗцŠøŠø %s + + + + + + Not enough parameters + ŠŠµŠ“Š¾ŃŃ‚Š°Ń‚Š¾Ń‡Š½Š¾ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š¾Š² + + + + + Invalid UUID parameter + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ UUID ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ + + + + Invalid invocation + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š²Ń‹Š·Š¾Š² + + + + + Format autodetect failed: %Rrc + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š°Š²Ń‚Š¾Š“ŠµŃ‚ŠµŠŗтŠøрŠ¾Š²Š°Ń‚ŃŒ фŠ¾Ń€Š¼Š°Ń‚: %Rrc + + + + + + + + Cannot create the virtual disk container: %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ сŠ¾Š·Š“Š°Ń‚ŃŒ ŠŗŠ¾Š½Ń‚ŠµŠ¹Š½ŠµŃ€ Š²ŠøртуŠ°Š»ŃŒŠ½Š¾Š³Š¾ Š“ŠøсŠŗŠ°: %Rrc + + + + + Cannot open the image: %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š¾Ń‚Šŗрыть Š¾Š±Ń€Š°Š·: %Rrc + + + + Cannot set a new UUID: %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š·Š°Š“Š°Ń‚ŃŒ Š½Š¾Š²Ń‹Š¹ UUID: %Rrc + + + + UUID changed to: %s + + UUID ŠøŠ·Š¼ŠµŠ½ŠµŠ½ Š½Š°: %s + + + + + The GPT header seems corrupt because it contains too many entries + ŠšŠ°Š¶ŠµŃ‚ся, GPT Š·Š°Š³Š¾Š»Š¾Š²Š¾Šŗ ŠæŠ¾Š²Ń€ŠµŠ¶Š“ŠµŠ½, ŠæŠ¾Ń‚Š¾Š¼Ńƒ чтŠ¾ Š¾Š½ сŠ¾Š“ŠµŃ€Š¶Šøт сŠ»ŠøшŠŗŠ¾Š¼ Š¼Š½Š¾Š³Š¾ эŠ»ŠµŠ¼ŠµŠ½Ń‚Š¾Š² + + + + Allocating memory for the GPT partitions entries failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š²Ń‹Š“ŠµŠ»Šøть ŠæŠ°Š¼ŃŃ‚ŃŒ Š“Š»Ń эŠ»ŠµŠ¼ŠµŠ½Ń‚Š¾Š² GPT рŠ°Š·Š“ŠµŠ»Š¾Š² + + + + Reading the partition table failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾Ń‡ŠøтŠ°Ń‚ŃŒ тŠ°Š±Š»Šøцу рŠ°Š·Š“ŠµŠ»Š¾Š² + + + + More than one extended partition + Š‘Š¾Š»ŠµŠµ Š¾Š“Š½Š¾Š³Š¾ рŠ°ŃŃˆŠøрŠµŠ½Š½Š¾Š³Š¾ рŠ°Š·Š“ŠµŠ»Š° + + + + Inconsistency for logical partition start + ŠŠµŃŠ¾Š³Š»Š°ŃŠ¾Š²Š°Š½Š½Š¾ŃŃ‚ŃŒ Š½Š°Ń‡Š°Š»Š° Š»Š¾Š³ŠøчŠµŃŠŗŠ¾Š³Š¾ рŠ°Š·Š“ŠµŠ»Š° + + + + Logical partition without magic + Š›Š¾Š³ŠøчŠµŃŠŗŠøŠ¹ рŠ°Š·Š“ŠµŠ» Š±ŠµŠ· magic + + + + Logical partition with type 0 encountered + Š”тŠ¾Š»ŠŗŠ½ŃƒŠ»Šøсь с Š»Š¾Š³ŠøчŠµŃŠŗŠøŠ¼ рŠ°Š·Š“ŠµŠ»Š¾Š¼ тŠøŠæŠ° 0 + + + + Invalid partition start offset + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ сŠ¼ŠµŃ‰ŠµŠ½ŠøŠµ Š½Š°Ń‡Š°Š»Š° рŠ°Š·Š“ŠµŠ»Š° + + + + Logical partition chain broken + Š¦ŠµŠæŠ¾Ń‡ŠŗŠ° Š»Š¾Š³ŠøчŠµŃŠŗŠøх рŠ°Š·Š“ŠµŠ»Š¾Š² ŠæŠ¾Š²Ń€ŠµŠ¶Š“ŠµŠ½Š° + + + + Two partitions start at the same place + Š”Š²Š° рŠ°Š·Š“ŠµŠ»Š° Š½Š°Ń‡ŠøŠ½Š°ŃŽŃ‚ся с Š¾Š“Š½Š¾Š³Š¾ Šø тŠ¾Š³Š¾ Š¶Šµ Š¼ŠµŃŃ‚Š° + + + + Partition starts at sector 0 + Š Š°Š·Š“ŠµŠ» Š½Š°Ń‡ŠøŠ½Š°ŠµŃ‚ся Š² сŠµŠŗтŠ¾Ń€Šµ 0 + + + + Overlapping GPT partitions + ŠŸŠµŃ€ŠµŠŗрыŠ²Š°ŃŽŃ‰ŠøŠµŃŃ GPT рŠ°Š·Š“ŠµŠ»Ń‹ + + + + Overlapping MBR partitions + ŠŸŠµŃ€ŠµŠŗрыŠ²Š°ŃŽŃ‰ŠøŠµŃŃ MBR рŠ°Š·Š“ŠµŠ»Ń‹ + + + + + + + + + + + + + + Missing argument to '%s' + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s' + + + + + + + + + + Invalid parameter '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ '%s' + + + + +Syntax error: %N + + +Š”ŠøŠ½Ń‚Š°ŠŗсŠøчŠµŃŠŗŠ°Ń Š¾ŃˆŠøŠ±ŠŗŠ°: %N + + + + + Invalid option -%c + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š°Ń Š¾ŠæцŠøя -%c + + + + Invalid option case %i + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š²Š°Ń€ŠøŠ°Š½Ń‚ Š¾ŠæцŠøŠø %i + + + + Unknown option: %s + ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Š°Ń Š¾ŠæцŠøя: %s + + + + Invalid argument format: %s + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ фŠ¾Ń€Š¼Š°Ń‚ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚Š°: %s + + + + + Mandatory parameter -rawdisk missing + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š¾Š±ŃŠ·Š°Ń‚ŠµŠ»ŃŒŠ½Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ -rawdisk + + + + Cannot open the raw disk: %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š¾Ń‚Šŗрыть raw Š“ŠøсŠŗ: %Rrc + + + + Number Type StartCHS EndCHS Size (MiB) Start (Sect) + + ŠŠ¾Š¼ŠµŃ€ Š¢ŠøŠæ ŠŠ°Ń‡CHS ŠšŠ¾Š½CHS Š Š°Š·Š¼ŠµŃ€ (MiB) Š”тŠ°Ń€Ń‚ (Š”ŠµŠŗт) + + + + + Mandatory parameter -filename missing + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š¾Š±ŃŠ·Š°Ń‚ŠµŠ»ŃŒŠ½Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ -filename + + + + The parameter -mbr is only valid when the parameter -partitions is also present + ŠŸŠ°Ń€Š°Š¼ŠµŃ‚Ń€ -mbr Š“ŠµŠ¹ŃŃ‚Š²ŠøтŠµŠ»ŠµŠ½ тŠ¾Š»ŃŒŠŗŠ¾ ŠŗŠ¾Š³Š“Š° тŠ°ŠŗŠ¶Šµ ŠæрŠøсутстŠ²ŃƒŠµŃ‚ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ -partitions + + + + Cannot open the raw disk '%s': %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š¾Ń‚Šŗрыть raw Š“ŠøсŠŗ '%s': %Rrc + + + + File '%s' is no fixed/removable medium device + Š¤Š°Š¹Š» '%s' Š½Šµ яŠ²Š»ŃŠµŃ‚ся устрŠ¾Š¹ŃŃ‚Š²Š¾Š¼ фŠøŠŗсŠøрŠ¾Š²Š°Š½Š½Š¾Š³Š¾/сŠ¼ŠµŠ½Š½Š¾Š³Š¾ Š½Š¾ŃŠøтŠµŠ»Ń + + + + The -relative parameter is invalid for raw disk %s + ŠŸŠ°Ń€Š°Š¼ŠµŃ‚Ń€ -relative Š½ŠµŠ“Š¾ŠæустŠøŠ¼ Š“Š»Ń raw Š“ŠøсŠŗŠ° %s + + + + Cannot get the geometry of the raw disk '%s': %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæŠ¾Š»ŃƒŃ‡Šøть Š³ŠµŠ¾Š¼ŠµŃ‚Ń€Šøю raw Š“ŠøсŠŗŠ° '%s': %Rrc + + + + + + The -relative parameter is invalid for raw images + ŠŸŠ°Ń€Š°Š¼ŠµŃ‚Ń€ -relative Š½ŠµŠ“Š¾ŠæустŠøŠ¼ Š“Š»Ń raw Š¾Š±Ń€Š°Š·Š¾Š² + + + + + + Cannot get the size of the raw disk '%s': %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæŠ¾Š»ŃƒŃ‡Šøть рŠ°Š·Š¼ŠµŃ€ raw Š“ŠøсŠŗŠ° '%s': %Rrc + + + + + + Failed to get size of file '%s': %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæŠ¾Š»ŃƒŃ‡Šøть рŠ°Š·Š¼ŠµŃ€ фŠ°Š¹Š»Š° '%s': %Rrc + + + + File '%s' is no block device + Š¤Š°Š¹Š» '%s' Š½Šµ яŠ²Š»ŃŠµŃ‚ся Š±Š»Š¾Ń‡Š½Ń‹Š¼ устрŠ¾Š¹ŃŃ‚Š²Š¾Š¼ + + + + + + + Failed to get file informtation for raw disk '%s': %Rrc + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š»ŃƒŃ‡Šøть ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†Šøю Š“Š»Ń raw Š“ŠøсŠŗŠ° '%s': %Rrc + + + + Cannot get the block size for file '%s': %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæŠ¾Š»ŃƒŃ‡Šøть рŠ°Š·Š¼ŠµŃ€ Š±Š»Š¾ŠŗŠ° фŠ°Š¹Š»Š° '%s': %Rrc + + + + + Cannot get the block count for file '%s': %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæŠ¾Š»ŃƒŃ‡Šøть ŠŗŠ¾Š»ŠøчŠµŃŃ‚Š²Š¾ Š±Š»Š¾ŠŗŠ¾Š² Š² фŠ°Š¹Š»Šµ '%s': %Rrc + + + + File '%s' is neither block device nor regular file + Š¤Š°Š¹Š» '%s' Š½Šµ яŠ²Š»ŃŠµŃ‚ся Š½Šø Š±Š»Š¾Ń‡Š½Ń‹Š¼ устрŠ¾Š¹ŃŃ‚Š²Š¾Š¼, Š½Šø рŠµŠ³ŃƒŠ»ŃŃ€Š½Ń‹Š¼ фŠ°Š¹Š»Š¾Š¼ + + + + File '%s' is no block or char device + Š¤Š°Š¹Š» '%s' Š½Šµ яŠ²Š»ŃŠµŃ‚ся Š½Šø Š±Š»Š¾Ń‡Š½Ń‹Š¼ Š½Šø сŠøŠ¼Š²Š¾Š»ŃŒŠ½Ń‹Š¼ устрŠ¾Š¹ŃŃ‚Š²Š¾Š¼ + + + + File '%s' is neither character device nor regular file + Š¤Š°Š¹Š» '%s' Š½Šµ яŠ²Š»ŃŠµŃ‚ся Š½Šø сŠøŠ¼Š²Š¾Š»ŃŒŠ½Ń‹Š¼ устрŠ¾Š¹ŃŃ‚Š²Š¾Š¼ Š½Šø рŠµŠ³ŃƒŠ»ŃŃ€Š½Ń‹Š¼ фŠ°Š¹Š»Š¾Š¼ + + + + Detected size of raw disk '%s' is %RU64, an invalid value + ŠžŠ±Š½Š°Ń€ŃƒŠ¶ŠµŠ½Š½Ń‹Š¹ рŠ°Š·Š¼ŠµŃ€ raw Š“ŠøсŠŗŠ° '%s' %RU64, чтŠ¾ Š½Šµ яŠ²Š»ŃŠµŃ‚ся Š“Š¾ŠæустŠøŠ¼Ń‹Š¼ Š·Š½Š°Ń‡ŠµŠ½ŠøŠµŠ¼ + + + + Incorrect value in partitions parameter + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ Š² ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Šµ partitions + + + + Incorrect separator in partitions parameter + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ рŠ°Š·Š“ŠµŠ»ŠøтŠµŠ»ŃŒ Š² ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Šµ partitions + + + + Cannot read the partition information from '%s' + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæрŠ¾Ń‡ŠøтŠ°Ń‚ŃŒ ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†Šøю Š¾ рŠ°Š·Š“ŠµŠ»Š°Ń… ŠøŠ· '%s' + + + + 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. + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ (Šø Š½Šµ Š½ŃƒŠ¶Š½Š¾) яŠ²Š½Š¾ Š“Š°Š²Š°Ń‚ŃŒ Š“Š¾ŃŃ‚ŃƒŠæ Šŗ рŠ°ŃŃ‰ŠøрŠµŠ½Š½Š¾Š¼Ńƒ рŠ°Š·Š“ŠµŠ»Ńƒ %u. Š•ŃŠ»Šø Š½ŃƒŠ¶Š½Š¾, Š“Š°Š¹Ń‚Šµ Š“Š¾ŃŃ‚ŃƒŠæ ŠŗŠ¾ Š²ŃŠµŠ¼ Š»Š¾Š³ŠøчŠµŃŠŗŠøŠ¼ рŠ°Š·Š“ŠµŠ»Š°Š¼ Š²Š½ŃƒŃ‚Ń€Šø рŠ°ŃŃˆŠøрŠµŠ½Š½Š¾Š³Š¾ рŠ°Š·Š“ŠµŠ»Š°. + + + + + Out of memory allocating the partition list for '%s' + ŠŠµŠ“Š¾ŃŃ‚Š°Ń‚Š¾Ń‡Š½Š¾ ŠæŠ°Š¼ŃŃ‚Šø ŠæŠ¾Š“ сŠæŠøсŠ¾Šŗ рŠ°Š·Š“ŠµŠ»Š¾Š² Š“Š»Ń '%s' + + + + Out of memory allocating the partition descriptor for '%s' + ŠŠµŠ“Š¾ŃŃ‚Š°Ń‚Š¾Ń‡Š½Š¾ ŠæŠ°Š¼ŃŃ‚Šø ŠæŠ¾Š“ Š“ŠµŃŠŗрŠøŠæтŠ¾Ń€ рŠ°Š·Š“ŠµŠ»Š° Š“Š»Ń '%s' + + + + Cannot read partition data from raw device '%s': %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæрŠ¾Ń‡ŠøтŠ°Ń‚ŃŒ Š“Š°Š½Š½Ń‹Šµ рŠ°Š·Š“ŠµŠ»Š° ŠøŠ· raw устŠ°Ń€Š¾Š¹ŃŃ‚Š²Š° '%s': %Rrc + + + + Cannot open replacement MBR file '%s' specified with -mbr: %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š¾Ń‚Šŗрыть фŠ°Š¹Š» Š·Š°Š¼ŠµŠ½Ń‹ MBR '%s', уŠŗŠ°Š·Š°Š½Š½Ń‹Š¹ чŠµŃ€ŠµŠ· -mbr: %Rrc + + + + Cannot read replacement MBR file '%s': %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæрŠ¾Ń‡ŠøтŠ°Ń‚ŃŒ фŠ°Š¹Š» Š·Š°Š¼ŠµŠ½Ń‹ MBR '%s': %Rrc + + + + Cannot create reference to individual partition %u, rc=%Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ сŠ¾Š·Š“Š°Ń‚ŃŒ ссыŠ»Šŗу Š½Š° ŠøŠ½Š“ŠøŠ²ŠøŠ“уŠ°Š»ŃŒŠ½Ń‹Š¹ рŠ°Š·Š“ŠµŠ» %u, rc=%Rrc + + + + Cannot create reference to individual partition %u (numbered %u), rc=%Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ сŠ¾Š·Š“Š°Ń‚ŃŒ ссыŠ»Šŗу Š½Š° ŠøŠ½Š“ŠøŠ²ŠøŠ“уŠ°Š»ŃŒŠ½Ń‹Š¹ рŠ°Š·Š“ŠµŠ» %u (ŠæрŠ¾Š½ŃƒŠ¼ŠµŃ€Š¾Š²Š°Š½Š½Ń‹Š¹ ŠŗŠ°Šŗ %u), rc=%Rrc + + + + MBR/EPT overlaps with data area + MBR/EPT ŠæŠµŃ€ŠµŠŗрыŠ²Š°ŠµŃ‚ Š¾Š±Š»Š°ŃŃ‚ŃŒ Š“Š°Š½Š½Ń‹Ń… + + + + GPT overlaps with data area + GPT ŠæŠµŃ€ŠµŠŗрыŠ²Š°ŠµŃ‚ Š¾Š±Š»Š°ŃŃ‚ŃŒ Š“Š°Š½Š½Ń‹Ń… + + + + Cannot create the raw disk VMDK: %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ сŠ¾Š·Š“Š°Ń‚ŃŒ VMDK raw Š“ŠøсŠŗ: %Rrc + + + + RAW host disk access VMDK file %s created successfully. + + VMDK фŠ°Š¹Š» %s Š“Š»Ń Š“Š¾ŃŃ‚ŃƒŠæŠ° Šŗ RAW Š“ŠøсŠŗу хŠ¾ŃŃ‚Š° сŠ¾Š·Š“Š°Š½ усŠæŠµŃˆŠ½Š¾. + + + + + The raw disk vmdk file was not created + vmdk фŠ°Š¹Š» Šŗ raw Š“ŠøсŠŗу Š½Šµ сŠ¾Š·Š“Š°Š½ + + + + Mandatory parameter -from missing + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š¾Š±ŃŠ·Š°Ń‚ŠµŠ»ŃŒŠ½Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ -from + + + + Mandatory parameter -to missing + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š¾Š±ŃŠ·Š°Ń‚ŠµŠ»ŃŒŠ½Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ -to + + + + Cannot rename the image: %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæŠµŃ€ŠµŠøŠ¼ŠµŠ½Š¾Š²Š°Ń‚ŃŒ Š¾Š±Ń€Š°Š·: %Rrc + + + + Cannot create the source image: %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ сŠ¾Š·Š“Š°Ń‚ŃŒ Š¾Š±Ń€Š°Š· ŠøстŠ¾Ń‡Š½ŠøŠŗŠ°: %Rrc + + + + Mandatory filename parameter missing + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š¾Š±ŃŠ·Š°Ń‚ŠµŠ»ŃŒŠ½Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ ŠøŠ¼Ń фŠ°Š¹Š»Š° + + + + Mandatory outputfile parameter missing + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š¾Š±ŃŠ·Š°Ń‚ŠµŠ»ŃŒŠ½Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€: Š²Ń‹Ń…Š¾Š“Š½Š¾Š¹ фŠ°Š¹Š» + + + + Cannot create destination file "%s": %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ сŠ¾Š·Š“Š°Ń‚ŃŒ фŠ°Š¹Š» Š½Š°Š·Š½Š°Ń‡ŠµŠ½Šøя "%s": %Rrc + + + + + + No file format specified and autodetect failed - please specify format: %Rrc + ŠŠµ уŠŗŠ°Š·Š°Š½ фŠ¾Ń€Š¼Š°Ń‚ фŠ°Š¹Š»Š°, тŠ°ŠŗŠ¶Šµ Š°Š²Ń‚Š¾Š“ŠµŃ‚ŠµŠŗт Š½Šµ ŠæрŠ¾ŃˆŠµŠ» - ŠæŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š°, уŠŗŠ°Š¶ŠøтŠµ фŠ¾Ń€Š¼Š°Ń‚: %Rrc + + + + Only converting harddisk images is supported + ŠŸŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŃ‚ся тŠ¾Š»ŃŒŠŗŠ¾ ŠæрŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Š½ŠøŠµ Š¾Š±Ń€Š°Š·Š¾Š² Š¶ŠµŃŃ‚ŠŗŠøх Š“ŠøсŠŗŠ¾Š² + + + + + Cannot open the source image: %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š¾Ń‚Šŗрыть Š¾Š±Ń€Š°Š· ŠøстŠ¾Ń‡Š½ŠøŠŗŠ°: %Rrc + + + + Converting image "%s" with size %RU64 bytes (%RU64MB) to raw... + + + ŠŸŃ€ŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Š½ŠøŠµ Š¾Š±Ń€Š°Š·Š° "%s" рŠ°Š·Š¼ŠµŃ€Š¾Š¼ %RU64 Š±Š°Š¹Ń‚ (%RU64MB) Š² raw... + + ŠŸŃ€ŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Š½ŠøŠµ Š¾Š±Ń€Š°Š·Š° "%s" рŠ°Š·Š¼ŠµŃ€Š¾Š¼ %RU64 Š±Š°Š¹Ń‚Š° (%RU64MB) Š² raw... + + ŠŸŃ€ŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Š½ŠøŠµ Š¾Š±Ń€Š°Š·Š° "%s" рŠ°Š·Š¼ŠµŃ€Š¾Š¼ %RU64 Š±Š°Š¹Ń‚ (%RU64MB) Š² raw... + + + + + + Cannot copy image data: %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ сŠŗŠ¾ŠæŠøрŠ¾Š²Š°Ń‚ŃŒ Š“Š°Š½Š½Ń‹Šµ Š¾Š±Ń€Š°Š·Š°: %Rrc + + + + Out of memory allocating read buffer + ŠŠµ хŠ²Š°Ń‚Š°ŠµŃ‚ ŠæŠ°Š¼ŃŃ‚Šø ŠæŠ¾Š“ Š±ŃƒŃ„ŠµŃ€ чтŠµŠ½Šøя + + + + + Mandatory input image parameter missing + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š¾Š±ŃŠ·Š°Ń‚ŠµŠ»ŃŒŠ½Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ Š²Ń…Š¾Š“Š½Š¾Š¹ Š¾Š±Ń€Š°Š· + + + + Mandatory output image parameter missing + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š¾Š±ŃŠ·Š°Ń‚ŠµŠ»ŃŒŠ½Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ Š²Ń‹Ń…Š¾Š“Š½Š¾Š¹ Š¾Š±Ń€Š°Š· + + + + Cannot create the source virtual disk container: %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ сŠ¾Š·Š“Š°Ń‚ŃŒ ŠŗŠ¾Š½Ń‚ŠµŠ¹Š½ŠµŃ€ ŠøсхŠ¾Š“Š½Š¾Š³Š¾ Š²ŠøртуŠ°Š»ŃŒŠ½Š¾Š³Š¾ Š“ŠøсŠŗŠ°: %Rrc + + + + Cannot create the destination virtual disk container: %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ сŠ¾Š·Š“Š°Ń‚ŃŒ ŠŗŠ¾Š½Ń‚ŠµŠ¹Š½ŠµŃ€ Š²ŠøртуŠ°Š»ŃŒŠ½Š¾Š³Š¾ Š“ŠøсŠŗŠ° Š½Š°Š·Š½Š°Ń‡ŠµŠ½Šøя: %Rrc + + + + Converting image "%s" with size %RU64 bytes (%RU64MB)... + + + ŠŸŃ€ŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Š½ŠøŠµ Š¾Š±Ń€Š°Š·Š° "%s" рŠ°Š·Š¼ŠµŃ€Š¾Š¼ %RU64 Š±Š°Š¹Ń‚ (%RU64MB)... + + ŠŸŃ€ŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Š½ŠøŠµ Š¾Š±Ń€Š°Š·Š° "%s" рŠ°Š·Š¼ŠµŃ€Š¾Š¼ %RU64 Š±Š°Š¹Ń‚Š° (%RU64MB)... + + ŠŸŃ€ŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Š½ŠøŠµ Š¾Š±Ń€Š°Š·Š° "%s" рŠ°Š·Š¼ŠµŃ€Š¾Š¼ %RU64 Š±Š°Š¹Ń‚ (%RU64MB)... + + + + + + Cannot copy the image: %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ сŠŗŠ¾ŠæŠøрŠ¾Š²Š°Ń‚ŃŒ Š¾Š±Ń€Š°Š·: %Rrc + + + + + Missing VM name/UUID + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ ŠøŠ¼Ń Š’Šœ/UUID + + + + One or more of the requested features are not implemented! Feel free to do this. + ŠžŠ“ŠøŠ½ ŠøŠ»Šø Š½ŠµŃŠŗŠ¾Š»ŃŒŠŗŠ¾ Š·Š°ŠæрŠ¾ŃˆŠµŠ½Š½Ń‹Ń… фуŠ½ŠŗцŠøŠ¹ Š½Šµ рŠµŠ°Š»ŠøŠ·Š¾Š²Š°Š½Ń‹! ŠŠµ стŠµŃŠ½ŃŠ¹Ń‚ŠµŃŃŒ сŠ“ŠµŠ»Š°Ń‚ŃŒ Šøх. + + + + password to hash required + трŠµŠ±ŃƒŠµŃ‚ся ŠæŠ°Ń€Š¾Š»ŃŒ Š“Š»Ń хŠµŃˆŠ° + + + + Password hash: %s + + Š„ŠµŃˆ ŠæŠ°Ń€Š¾Š»Ń: %s + + + + + Invalid update interval specified + Š£ŠŗŠ°Š·Š°Š½ Š½ŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠøŠ½Ń‚ŠµŃ€Š²Š°Š» Š¾Š±Š½Š¾Š²Š»ŠµŠ½Šøя + + + + argc=%d interval=%u + + argc=%d ŠøŠ½Ń‚ŠµŃ€Š²Š°Š»=%u + + + + + Command missing + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ ŠŗŠ¾Š¼Š°Š½Š“Š° + + + + Invalid command '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š°Ń ŠŗŠ¾Š¼Š°Š½Š“Š° '%s' + + + + List + + + + + + + Unknown + ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Š¾ + + + + + unknown + Š½ŠµŠøŠ·Š²ŠµŃŃ‚Š½Š¾ + + + + Up + Š’ŠŗŠ»ŃŽŃ‡ŠµŠ½ + + + + Down + Š’Ń‹ŠŗŠ»ŃŽŃ‡ŠµŠ½ + + + + HardDisk + Š–ŠµŃŃ‚ŠŗŠøŠ¹ Š”ŠøсŠŗ + + + + Floppy + Š¤Š»Š¾ŠæŠæŠø + + + + Network + Š”ŠµŃ‚ŃŒ + + + + SharedFolder + ŠžŠ±Ń‰Š°Ń ŠæŠ°ŠæŠŗŠ° + + + + Graphics3D + 3D Š³Ń€Š°Ń„ŠøŠŗŠ° + + + + + Name: %ls + + Š˜Š¼Ń: %ls + + + + + + + + Name: %ls + + Š˜Š¼Ń: %ls + + + + + + + Enabled + Š’ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾ + + + + + + Disabled + ŠžŃ‚ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾ + + + + IPAddress: %ls + + IP Š°Š“рŠµŃ: %ls + + + + + + NetworkMask: %ls + + Š”ŠµŃ‚ŠµŠ²Š°Ń Š¼Š°ŃŠŗŠ°: %ls + + + + + IPV6Address: %ls + + IPV6 Š°Š“рŠµŃ: %ls + + + + + IPV6NetworkMaskPrefixLength: %d + + Š”Š»ŠøŠ½Š° ŠæрŠµŃ„ŠøŠŗсŠ° сŠµŃ‚ŠµŠ²Š¾Š¹ Š¼Š°ŃŠŗŠø IPV6: %d + + + + + HardwareAddress: %ls + + ŠŠæŠæŠ°Ń€Š°Ń‚Š½Ń‹Š¹ Š°Š“рŠµŃ: %ls + + + + + MediumType: %s + + Š¢ŠøŠæ Š½Š¾ŃŠøтŠµŠ»Ń: %s + + + + + Wireless: %s + + Š‘ŠµŃŠæрŠ¾Š²Š¾Š“Š½Ń‹Š¹: %s + + + + + + Yes + Š”Š° + + + + + No + ŠŠµŃ‚ + + + + Status: %s + + Š”тŠ°Ń‚ŃƒŃ: %s + + + + + + + VBoxNetworkName: %ls + + + Š˜Š¼Ń сŠµŃ‚Šø VBox: %ls + + + + + + + State: %s + + Š”Š¾ŃŃ‚Š¾ŃŠ½ŠøŠµ: %s + + + + + LowerIP: %ls + + ŠŠøŠ¶Š½ŠøŠ¹ IP: %ls + + + + + UpperIP: %ls + + Š’ŠµŃ€Ń…Š½ŠøŠ¹ IP: %ls + + + + + CloudProvider: %ls + + ŠžŠ±Š»Š°Ń‡Š½Ń‹Š¹ ŠæрŠ¾Š²Š°Š¹Š“ŠµŃ€: %ls + + + + + CloudProfile: %ls + + ŠžŠ±Š»Š°Ń‡Š½Ń‹Š¹ ŠæрŠ¾Ń„ŠøŠ»ŃŒ: %ls + + + + + CloudNetworkId: %ls + + ID Š¾Š±Š»Š°Ń‡Š½Š¾Š¹ сŠµŃ‚Šø: %ls + + + + + HW virtualization + ŠŠæŠæŠ°Ń€Š°Ń‚Š½Š°Ń Š²ŠøртуŠ°Š»ŠøŠ·Š°Ń†Šøя + + + + long mode + Š“Š»ŠøŠ½Š½Ń‹Š¹ рŠµŠ¶ŠøŠ¼ + + + + nested paging + Š²Š»Š¾Š¶ŠµŠ½Š½Ń‹Šµ стрŠ°Š½Šøцы + + + + unrestricted guest + Š½ŠµŠ¾Š³Ń€Š°Š½ŠøчŠµŠ½Š½Ń‹Š¹ Š³Š¾ŃŃ‚ŃŒ + + + + nested HW virtualization + Š²Š»Š¾Š¶ŠµŠ½Š½Š°Ń Š°ŠæŠæŠ°Ń€Š°Ń‚Š½Š°Ń Š²ŠøртуŠ°Š»ŠøŠ·Š°Ń†Šøя + + + + virt. vmsave/vmload + virt. vmsave/vmload + + + + Host Information: + + + Š˜Š½Ń„Š¾Ń€Š¼Š°Ń†Šøя Š¾ хŠ¾ŃŃ‚Šµ: + + + + + + Host time: %s + + Š’Ń€ŠµŠ¼Ń хŠ¾ŃŃ‚Š°: %s + + + + + Processor online count: %lu + + Š§ŠøсŠ»Š¾ Š°ŠŗтŠøŠ²Š½Ń‹Ń… ŠæрŠ¾Ń†ŠµŃŃŠ¾Ń€Š¾Š²: %lu + + + + + Processor count: %lu + + Š§ŠøсŠ»Š¾ ŠæрŠ¾Ń†ŠµŃŃŠ¾Ń€Š¾Š²: %lu + + + + + Processor online core count: %lu + + Š§ŠøсŠ»Š¾ Š°ŠŗтŠøŠ²Š½Ń‹Ń… яŠ“ŠµŃ€ ŠæрŠ¾Ń†ŠµŃŃŠ¾Ń€Š¾Š²: %lu + + + + + Processor core count: %lu + + Š§ŠøсŠ»Š¾ яŠ“ŠµŃ€ ŠæрŠ¾Ń†ŠµŃŃŠ¾Ń€Š¾Š²: %lu + + + + + Processor supports %s: %s + + ŠŸŃ€Š¾Ń†ŠµŃŃŠ¾Ń€ ŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŃ‚ %s: %s + + + + + + + + + + yes + Š“Š° + + + + + + + + + no + Š½ŠµŃ‚ + + + + Processor#%u speed: %lu MHz + + Š”ŠŗŠ¾Ń€Š¾ŃŃ‚ŃŒ ŠæрŠ¾Ń†ŠµŃŃŠ¾Ń€Š° #%u: %lu ŠœŠ“ц + + + + + Processor#%u speed: unknown + + Š”ŠŗŠ¾Ń€Š¾ŃŃ‚ŃŒ ŠæрŠ¾Ń†ŠµŃŃŠ¾Ń€Š° #%u: Š½ŠµŠøŠ·Š²ŠµŃŃ‚Š½Š¾ + + + + + Processor#%u description: %ls + + ŠžŠæŠøсŠ°Š½ŠøŠµ ŠæрŠ¾Ń†ŠµŃŃŠ¾Ń€Š° #%u: %ls + + + + + Memory size: %lu MByte + + + Š Š°Š·Š¼ŠµŃ€ ŠæŠ°Š¼ŃŃ‚Šø: %lu ŠœŠ‘Š°Š¹Ń‚ + + Š Š°Š·Š¼ŠµŃ€ ŠæŠ°Š¼ŃŃ‚Šø: %lu ŠœŠ‘Š°Š¹Ń‚Š° + + Š Š°Š·Š¼ŠµŃ€ ŠæŠ°Š¼ŃŃ‚Šø: %lu ŠœŠ‘Š°Š¹Ń‚ + + + + + + Memory available: %lu MByte + + + Š”Š¾ŃŃ‚ŃƒŠæŠ½Š¾ ŠæŠ°Š¼ŃŃ‚Šø: %lu ŠœŠ‘Š°Š¹Ń‚ + + Š”Š¾ŃŃ‚ŃƒŠæŠ½Š¾ ŠæŠ°Š¼ŃŃ‚Šø: %lu ŠœŠ‘Š°Š¹Ń‚Š° + + Š”Š¾ŃŃ‚ŃƒŠæŠ½Š¾ ŠæŠ°Š¼ŃŃ‚Šø: %lu ŠœŠ‘Š°Š¹Ń‚ + + + + + + Operating system: %ls + + ŠžŠæŠµŃ€Š°Ń†ŠøŠ¾Š½Š½Š°Ń сŠøстŠµŠ¼Š°: %ls + + + + + Operating system version: %ls + + Š’ŠµŃ€ŃŠøя Š¾ŠæŠµŃ€Š°Ń†ŠøŠ¾Š½Š½Š¾Š¹ сŠøстŠµŠ¼Ń‹: %ls + + + + + Supported hard disk backends: + + + ŠŸŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŠ¼Ń‹Šµ Š±ŃŠŗŠµŠ½Š“ы Š¶ŠµŃŃ‚ŠŗŠøх Š“ŠøсŠŗŠ¾Š²: + + + + + + Backend %u: id='%ls' description='%ls' capabilities=%#06x extensions=' + Š‘эŠŗŠµŠ½Š“ %u: id='%ls' Š¾ŠæŠøсŠ°Š½ŠøŠµ='%ls' Š²Š¾Š·Š¼Š¾Š¶Š½Š¾ŃŃ‚Šø=%#06x рŠ°ŃŃˆŠøрŠµŠ½Šøя=' + + + + properties=( + сŠ²Š¾Š¹ŃŃ‚Š²Š°=( + + + + + name='%ls' desc='%ls' type= + + ŠøŠ¼Ń='%ls' Š¾ŠæŠøсŠ°Š½ŠøŠµ='%ls' тŠøŠæ= + + + + int + int + + + + byte + byte + + + + string + string + + + + flags=%#04x + фŠ»Š°Š³Šø=%#04x + + + + default='%ls' + ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю='%ls' + + + + Host USB Devices: + + + USB устрŠ¾Š¹ŃŃ‚Š²Š° хŠ¾ŃŃ‚Š°: + + + + + + + <none> + + + <Š½ŠµŃ‚> + + + + + + UUID: %s +VendorId: %#06x (%04X) +ProductId: %#06x (%04X) +Revision: %u.%u (%02u%02u) +Port: %u + + UUID: %s +ID ŠŸŠ¾ŃŃ‚Š°Š²Ń‰ŠøŠŗŠ°: %#06x (%04X) +ID ŠŸŃ€Š¾Š“уŠŗтŠ°: %#06x (%04X) +Š ŠµŠ²ŠøŠ·Šøя: %u.%u (%02u%02u) +ŠŸŠ¾Ń€Ń‚: %u + + + + + Low + ŠŠøŠ·ŠŗŠ°Ń + + + + Full + ŠŸŠ¾Š»Š½Š°Ń + + + + High + Š’ысŠ¾ŠŗŠ°Ń + + + + Super + Š”уŠæŠµŃ€ + + + + SuperPlus + Š”уŠæŠµŃ€ŠŸŠ»ŃŽŃ + + + + USB version/speed: %u/%s + + Š’ŠµŃ€ŃŠøя/сŠŗŠ¾Ń€Š¾ŃŃ‚ŃŒ USB: %u/%s + + + + + Manufacturer: %ls + + ŠŸŃ€Š¾ŠøŠ·Š²Š¾Š“ŠøтŠµŠ»ŃŒ: %ls + + + + + Product: %ls + + ŠŸŃ€Š¾Š“уŠŗт: %ls + + + + + SerialNumber: %ls + + Š”ŠµŃ€ŠøŠ¹Š½Ń‹Š¹ Š½Š¾Š¼ŠµŃ€: %ls + + + + + Address: %ls + + ŠŠ“рŠµŃ: %ls + + + + + Port path: %ls + + ŠŸŃƒŃ‚ŃŒ Šŗ ŠæŠ¾Ń€Ń‚Ńƒ: %ls + + + + + Not supported + ŠŠµ ŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŃ‚ся + + + + Unavailable + ŠŠµŠ“Š¾ŃŃ‚ŃƒŠæŠµŠ½ + + + + Busy + Š—Š°Š½ŃŃ‚ + + + + Available + Š”Š¾ŃŃ‚ŃƒŠæŠµŠ½ + + + + Held + Š£Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŃ‚ся + + + + Captured + Š—Š°Ń…Š²Š°Ń‡ŠµŠ½ + + + + Current State: %s + + + Š¢ŠµŠŗущŠµŠµ сŠ¾ŃŃ‚Š¾ŃŠ½ŠøŠµ: %s + + + + + + Global USB Device Filters: + + + Š“Š»Š¾Š±Š°Š»ŃŒŠ½Ń‹Šµ фŠøŠ»ŃŒŃ‚ры USB устрŠ¾Š¹ŃŃ‚Š²: + + + + + + Index: %zu + + Š˜Š½Š“ŠµŠŗс: %zu + + + + + Active: %s + + ŠŠŗтŠøŠ²Š½Š¾: %s + + + + + <invalid> + <Š½ŠµŠ“ŠµŠ¹ŃŃ‚Š²ŠøтŠµŠ»ŃŒŠ½Š¾> + + + + Ignore + Š˜Š³Š½Š¾Ń€ŠøрŠ¾Š²Š°Ń‚ŃŒ + + + + Hold + Š£Š“ŠµŃ€Š¶ŠøŠ²Š°Ń‚ŃŒ + + + + Action: %s + + Š”ŠµŠ¹ŃŃ‚Š²ŠøŠµ: %s + + + + + Name: %ls + + Š˜Š¼Ń: %ls + + + + + VendorId: %ls + + ID ŠŸŠ¾ŃŃ‚Š°Š²Ń‰ŠøŠŗŠ°: %ls + + + + + ProductId: %ls + + ID ŠŸŃ€Š¾Š“уŠŗтŠ°: %ls + + + + + Revision: %ls + + Š ŠµŠ²ŠøŠ·Šøя: %ls + + + + + Manufacturer: %ls + + ŠŸŃ€Š¾ŠøŠ·Š²Š¾Š“ŠøтŠµŠ»ŃŒ: %ls + + + + + Product: %ls + + ŠŸŃ€Š¾Š“уŠŗт: %ls + + + + + Serial Number: %ls + + + Š”ŠµŃ€ŠøŠ¹Š½Ń‹Š¹ Š½Š¾Š¼ŠµŃ€: %ls + + + + + + API version: %ls + + Š’ŠµŃ€ŃŠøя API: %ls + + + + + Minimum guest RAM size: %u Megabytes + + + ŠœŠøŠ½ŠøŠ¼Š°Š»ŃŒŠ½Ń‹Š¹ рŠ°Š·Š¼ŠµŃ€ Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ RAM: %u ŠœŠµŠ³Š°Š±Š°Š¹Ń‚ + + ŠœŠøŠ½ŠøŠ¼Š°Š»ŃŒŠ½Ń‹Š¹ рŠ°Š·Š¼ŠµŃ€ Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ RAM: %u ŠœŠµŠ³Š°Š±Š°Š¹Ń‚Š° + + ŠœŠøŠ½ŠøŠ¼Š°Š»ŃŒŠ½Ń‹Š¹ рŠ°Š·Š¼ŠµŃ€ Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ RAM: %u ŠœŠµŠ³Š°Š±Š°Š¹Ń‚ + + + + + + Maximum guest RAM size: %u Megabytes + + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Ń‹Š¹ рŠ°Š·Š¼ŠµŃ€ Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ RAM: %u ŠœŠµŠ³Š°Š±Š°Š¹Ń‚ + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Ń‹Š¹ рŠ°Š·Š¼ŠµŃ€ Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ RAM: %u ŠœŠµŠ³Š°Š±Š°Š¹Ń‚Š° + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Ń‹Š¹ рŠ°Š·Š¼ŠµŃ€ Š³Š¾ŃŃ‚ŠµŠ²Š¾Š¹ RAM: %u ŠœŠµŠ³Š°Š±Š°Š¹Ń‚ + + + + + + Minimum video RAM size: %u Megabytes + + + ŠœŠøŠ½ŠøŠ¼Š°Š»ŃŒŠ½Ń‹Š¹ рŠ°Š·Š¼ŠµŃ€ Š²ŠøŠ“ŠµŠ¾ RAM: %u ŠœŠµŠ³Š°Š±Š°Š¹Ń‚ + + ŠœŠøŠ½ŠøŠ¼Š°Š»ŃŒŠ½Ń‹Š¹ рŠ°Š·Š¼ŠµŃ€ Š²ŠøŠ“ŠµŠ¾ RAM: %u ŠœŠµŠ³Š°Š±Š°Š¹Ń‚Š° + + ŠœŠøŠ½ŠøŠ¼Š°Š»ŃŒŠ½Ń‹Š¹ рŠ°Š·Š¼ŠµŃ€ Š²ŠøŠ“ŠµŠ¾ RAM: %u ŠœŠµŠ³Š°Š±Š°Š¹Ń‚ + + + + + + Maximum video RAM size: %u Megabytes + + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Ń‹Š¹ рŠ°Š·Š¼ŠµŃ€ Š²ŠøŠ“ŠµŠ¾ RAM: %u ŠœŠµŠ³Š°Š±Š°Š¹Ń‚ + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Ń‹Š¹ рŠ°Š·Š¼ŠµŃ€ Š²ŠøŠ“ŠµŠ¾ RAM: %u ŠœŠµŠ³Š°Š±Š°Š¹Ń‚Š° + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Ń‹Š¹ рŠ°Š·Š¼ŠµŃ€ Š²ŠøŠ“ŠµŠ¾ RAM: %u ŠœŠµŠ³Š°Š±Š°Š¹Ń‚ + + + + + + Maximum guest monitor count: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ Š³Š¾ŃŃ‚ŠµŠ²Ń‹Ń… Š¼Š¾Š½ŠøтŠ¾Ń€Š¾Š²: %u + + + + + Minimum guest CPU count: %u + + ŠœŠøŠ½ŠøŠ¼Š°Š»ŃŒŠ½Š¾ Š³Š¾ŃŃ‚ŠµŠ²Ń‹Ń… Š¦ŠŸŠ£: %u + + + + + Maximum guest CPU count: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ Š³Š¾ŃŃ‚ŠµŠ²Ń‹Ń… Š¦ŠŸŠ£: %u + + + + + Virtual disk limit (info): %lld Bytes + + + ŠžŠ³Ń€Š°Š½ŠøчŠµŠ½ŠøŠµ Š²ŠøртуŠ°Š»ŃŒŠ½Š¾Š³Š¾ Š“ŠøсŠŗŠ° (ŠøŠ½Ń„Š¾): %lld Š‘Š°Š¹Ń‚ + + ŠžŠ³Ń€Š°Š½ŠøчŠµŠ½ŠøŠµ Š²ŠøртуŠ°Š»ŃŒŠ½Š¾Š³Š¾ Š“ŠøсŠŗŠ° (ŠøŠ½Ń„Š¾): %lld Š‘Š°Š¹Ń‚Š° + + ŠžŠ³Ń€Š°Š½ŠøчŠµŠ½ŠøŠµ Š²ŠøртуŠ°Š»ŃŒŠ½Š¾Š³Š¾ Š“ŠøсŠŗŠ° (ŠøŠ½Ń„Š¾): %lld Š‘Š°Š¹Ń‚ + + + + + + Maximum Serial Port count: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ ŠæŠ¾ŃŠ»ŠµŠ“Š¾Š²Š°Ń‚ŠµŠ»ŃŒŠ½Ń‹Ń… ŠæŠ¾Ń€Ń‚Š¾Š²: %u + + + + + Maximum Parallel Port count: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ ŠæŠ°Ń€Š°Š»Š»ŠµŠ»ŃŒŠ½Ń‹Ń… ŠæŠ¾Ń€Ń‚Š¾Š²: %u + + + + + Maximum Boot Position: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š°Ń Š·Š°Š³Ń€ŃƒŠ·Š¾Ń‡Š½Š°Ń ŠæŠ¾Š·ŠøцŠøя: %u + + + + + Maximum PIIX3 Network Adapter count: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ PIIX3 сŠµŃ‚ŠµŠ²Ń‹Ń… Š°Š“Š°ŠæтŠµŃ€Š¾Š²: %u + + + + + Maximum ICH9 Network Adapter count: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ ICH9 сŠµŃ‚ŠµŠ²Ń‹Ń… Š°Š“Š°ŠæтŠµŃ€Š¾Š²: %u + + + + + Maximum PIIX3 IDE Controllers: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ PIIX3 IDE ŠšŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€Š¾Š²: %u + + + + + Maximum ICH9 IDE Controllers: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ ICH9 IDE ŠšŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€Š¾Š²: %u + + + + + Maximum IDE Port count: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ IDE ŠæŠ¾Ń€Ń‚Š¾Š²: %u + + + + + Maximum Devices per IDE Port: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ устрŠ¾Š¹ŃŃ‚Š² Š½Š° IDE ŠæŠ¾Ń€Ń‚: %u + + + + + Maximum PIIX3 SATA Controllers: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ PIIX3 SATA ŠšŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€Š¾Š²: %u + + + + + Maximum ICH9 SATA Controllers: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ ICH9 SATA ŠšŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€Š¾Š²: %u + + + + + Maximum SATA Port count: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ SATA ŠæŠ¾Ń€Ń‚Š¾Š²: %u + + + + + Maximum Devices per SATA Port: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ устрŠ¾Š¹ŃŃ‚Š² Š½Š° SATA ŠæŠ¾Ń€Ń‚: %u + + + + + Maximum PIIX3 SCSI Controllers: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ PIIX3 SCSI ŠšŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€Š¾Š²: %u + + + + + Maximum ICH9 SCSI Controllers: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ ICH9 SCSI ŠšŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€Š¾Š²: %u + + + + + Maximum SCSI Port count: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ SCSI ŠæŠ¾Ń€Ń‚Š¾Š²: %u + + + + + Maximum Devices per SCSI Port: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ устрŠ¾Š¹ŃŃ‚Š² Š½Š° SCSI ŠæŠ¾Ń€Ń‚: %u + + + + + Maximum SAS PIIX3 Controllers: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ PIIX3 SAS ŠšŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€Š¾Š²: %u + + + + + Maximum SAS ICH9 Controllers: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ ICH9 SAS ŠšŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€Š¾Š²: %u + + + + + Maximum SAS Port count: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ SAS ŠæŠ¾Ń€Ń‚Š¾Š²: %u + + + + + Maximum Devices per SAS Port: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ устрŠ¾Š¹ŃŃ‚Š² Š½Š° SAS ŠæŠ¾Ń€Ń‚: %u + + + + + Maximum NVMe PIIX3 Controllers: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ PIIX3 NVMe ŠšŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€Š¾Š²: %u + + + + + Maximum NVMe ICH9 Controllers: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ ICH9 NVMe ŠšŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€Š¾Š²: %u + + + + + Maximum NVMe Port count: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ NVMe ŠæŠ¾Ń€Ń‚Š¾Š²: %u + + + + + Maximum Devices per NVMe Port: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ устрŠ¾Š¹ŃŃ‚Š² Š½Š° NVMe ŠæŠ¾Ń€Ń‚: %u + + + + + Maximum virtio-scsi PIIX3 Controllers: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ PIIX3 virtio-scsi ŠšŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€Š¾Š²: %u + + + + + Maximum virtio-scsi ICH9 Controllers: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ ICH9 virtio-scsi ŠšŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€Š¾Š²: %u + + + + + Maximum virtio-scsi Port count: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ virtio-scsi ŠæŠ¾Ń€Ń‚Š¾Š²: %u + + + + + Maximum Devices per virtio-scsi Port: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ устрŠ¾Š¹ŃŃ‚Š² Š½Š° virtio-scsi ŠæŠ¾Ń€Ń‚: %u + + + + + Maximum PIIX3 Floppy Controllers:%u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ PIIX3 Š¤Š»Š¾ŠæŠæŠø ŠšŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€Š¾Š²: %u + + + + + Maximum ICH9 Floppy Controllers: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ ICH9 Š¤Š»Š¾ŠæŠæŠø ŠšŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€Š¾Š²: %u + + + + + Maximum Floppy Port count: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ Š¤Š»Š¾ŠæŠæŠø ŠæŠ¾Ń€Ń‚Š¾Š²: %u + + + + + Maximum Devices per Floppy Port: %u + + ŠœŠ°ŠŗсŠøŠ¼Š°Š»ŃŒŠ½Š¾ устрŠ¾Š¹ŃŃ‚Š² Š½Š° Š¤Š»Š¾ŠæŠæŠø ŠæŠ¾Ń€Ń‚: %u + + + + + Free disk space warning at: %u Bytes + + + Š”Š°Ń‚ŃŒ ŠæрŠµŠ“уŠæрŠµŠ¶Š“ŠµŠ½ŠøŠµ ŠŗŠ¾Š³Š“Š° Š½Š° Š“ŠøсŠŗŠµ Š¾ŃŃ‚Š°Š»Š¾ŃŃŒ сŠ²Š¾Š±Š¾Š“Š½Š¾: %u Š±Š°Š¹Ń‚ + + Š”Š°Ń‚ŃŒ ŠæрŠµŠ“уŠæрŠµŠ¶Š“ŠµŠ½ŠøŠµ ŠŗŠ¾Š³Š“Š° Š½Š° Š“ŠøсŠŗŠµ Š¾ŃŃ‚Š°Š»Š¾ŃŃŒ сŠ²Š¾Š±Š¾Š“Š½Š¾: %u Š±Š°Š¹Ń‚Š° + + Š”Š°Ń‚ŃŒ ŠæрŠµŠ“уŠæрŠµŠ¶Š“ŠµŠ½ŠøŠµ ŠŗŠ¾Š³Š“Š° Š½Š° Š“ŠøсŠŗŠµ Š¾ŃŃ‚Š°Š»Š¾ŃŃŒ сŠ²Š¾Š±Š¾Š“Š½Š¾: %u Š±Š°Š¹Ń‚ + + + + + + Free disk space warning at: %u %% + + Š”Š°Ń‚ŃŒ ŠæрŠµŠ“уŠæрŠµŠ¶Š“ŠµŠ½ŠøŠµ ŠŗŠ¾Š³Š“Š° Š½Š° Š“ŠøсŠŗŠµ Š¾ŃŃ‚Š°Š»Š¾ŃŃŒ сŠ²Š¾Š±Š¾Š“Š½Š¾: %u %% + + + + + Free disk space error at: %u Bytes + + + Š”Š°Ń‚ŃŒ Š¾ŃˆŠøŠ±Šŗу ŠŗŠ¾Š³Š“Š° Š½Š° Š“ŠøсŠŗŠµ Š¾ŃŃ‚Š°Š»Š¾ŃŃŒ сŠ²Š¾Š±Š¾Š“Š½Š¾: %u Š±Š°Š¹Ń‚ + + Š”Š°Ń‚ŃŒ Š¾ŃˆŠøŠ±Šŗу ŠŗŠ¾Š³Š“Š° Š½Š° Š“ŠøсŠŗŠµ Š¾ŃŃ‚Š°Š»Š¾ŃŃŒ сŠ²Š¾Š±Š¾Š“Š½Š¾: %u Š±Š°Š¹Ń‚Š° + + Š”Š°Ń‚ŃŒ Š¾ŃˆŠøŠ±Šŗу ŠŗŠ¾Š³Š“Š° Š½Š° Š“ŠøсŠŗŠµ Š¾ŃŃ‚Š°Š»Š¾ŃŃŒ сŠ²Š¾Š±Š¾Š“Š½Š¾: %u Š±Š°Š¹Ń‚ + + + + + + Free disk space error at: %u %% + + Š”Š°Ń‚ŃŒ Š¾ŃˆŠøŠ±Šŗу ŠŗŠ¾Š³Š“Š° Š½Š° Š“ŠøсŠŗŠµ Š¾ŃŃ‚Š°Š»Š¾ŃŃŒ сŠ²Š¾Š±Š¾Š“Š½Š¾: %u %% + + + + + Default machine folder: %ls + + ŠŸŠ°ŠæŠŗŠ° Š¼Š°ŃˆŠøŠ½Ń‹ ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю: %ls + + + + + Raw-mode Supported: %s + + Raw-рŠµŠ¶ŠøŠ¼ ŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŃ‚ся: %s + + + + + Exclusive HW virtualization use: %s + + Š­ŠŗсŠŗŠ»ŃŽŠ·ŠøŠ²Š½Š¾Šµ ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š½ŠøŠµ Š°ŠæŠæŠ°Ń€Š°Ń‚Š½Š¾Š¹ Š²ŠøртуŠ°Š»ŠøŠ·Š°Ń†ŠøŠø: %s + + + + + + + on + Š²ŠŗŠ» + + + + + + off + Š²Ń‹ŠŗŠ» + + + + Default hard disk format: %ls + + Š¤Š¾Ń€Š¼Š°Ń‚ Š¶ŠµŃŃ‚ŠŗŠ¾Š³Š¾ Š“ŠøсŠŗŠ° ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю: %ls + + + + + VRDE auth library: %ls + + Š‘ŠøŠ±Š»ŠøŠ¾Ń‚ŠµŠŗŠ° Š°ŃƒŃ‚ŠµŠ½Ń‚ŠøфŠøŠŗŠ°Ń†ŠøŠø VRDE: %ls + + + + + Webservice auth. library: %ls + + Š‘ŠøŠ±Š»ŠøŠ¾Ń‚ŠµŠŗŠ° Š°ŃƒŃ‚ŠµŠ½Ń‚ŠøфŠøŠŗŠ°Ń†ŠøŠø Š²ŠµŠ±ŃŠµŃ€Š²ŠøсŠ°: %ls + + + + + Remote desktop ExtPack: %ls + + ŠŸŠ°ŠŗŠµŃ‚ рŠ°ŃŃˆŠøрŠµŠ½Šøя уŠ“Š°Š»ŠµŠ½Š½Š¾Š³Š¾ рŠ°Š±Š¾Ń‡ŠµŠ³Š¾ стŠ¾Š»Š°: %ls + + + + + Log history count: %u + + Š Š°Š·Š¼ŠµŃ€ ŠøстŠ¾Ń€ŠøŠø Š² Š¶ŃƒŃ€Š½Š°Š»Šµ: %u + + + + + Default frontend: %ls + + Š¤Ń€Š¾Š½Ń‚эŠ½Š“ ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю: %ls + + + + + Null + ŠŸŃƒŃŃ‚Š¾ + + + + Default audio driver: %s + + ŠŃƒŠ“ŠøŠ¾ Š“рŠ°Š¹Š²ŠµŃ€ ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю: %s + + + + + Autostart database path: %ls + + ŠŸŃƒŃ‚ŃŒ Š±Š°Š·Ń‹ Š“Š°Š½Š½Ń‹Ń… Š°Š²Ń‚Š¾ŃŃ‚Š°Ń€Ń‚Š°: %ls + + + + + Default Guest Additions ISO: %ls + + ISO Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½ŠøŠ¹ Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ” ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю: %ls + + + + + Logging Level: %ls + + Š£Ń€Š¾Š²ŠµŠ½ŃŒ Š¶ŃƒŃ€Š½Š°Š»Š°: %ls + + + + + System + Š”ŠøстŠµŠ¼Š° + + + + NoProxy + Š‘ŠµŠ· ŠŸŃ€Š¾ŠŗсŠø + + + + Manual + Š’Ń€ŃƒŃ‡Š½ŃƒŃŽ + + + + Proxy Mode: %s + + Š ŠµŠ¶ŠøŠ¼ ŠŸŃ€Š¾ŠŗсŠø: %s + + + + + Proxy URL: %ls + + URL ŠŸŃ€Š¾ŠŗсŠø: %ls + + + + Update check enabled: %s + + ŠŸŃ€Š¾Š²ŠµŃ€ŠŗŠ° Š¾Š±Š½Š¾Š²Š»ŠµŠ½ŠøŠ¹ Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½Š°: %s + + + + Update check count: %u + + ŠšŠ¾Š»ŠøчŠµŃŃ‚Š²Š¾ ŠæрŠ¾Š²ŠµŃ€Š¾Šŗ Š¾Š±Š½Š¾Š²Š»ŠµŠ½ŠøŠ¹: %u + + + + Update check frequency: never + + Š§Š°ŃŃ‚Š¾Ń‚Š° ŠæрŠ¾Š²ŠµŃ€ŠŗŠø Š¾Š±Š½Š¾Š²Š»ŠµŠ½ŠøŠ¹: Š½ŠøŠŗŠ¾Š³Š“Š° + + + + Update check frequency: every day + + Š§Š°ŃŃ‚Š¾Ń‚Š° ŠæрŠ¾Š²ŠµŃ€ŠŗŠø Š¾Š±Š½Š¾Š²Š»ŠµŠ½ŠøŠ¹: ŠŗŠ°Š¶Š“ыŠ¹ Š“ŠµŠ½ŃŒ + + + + Update check frequency: every %u days + + + Š§Š°ŃŃ‚Š¾Ń‚Š° ŠæрŠ¾Š²ŠµŃ€ŠŗŠø Š¾Š±Š½Š¾Š²Š»ŠµŠ½ŠøŠ¹: ŠŗŠ°Š¶Š“ыŠ¹ %u Š“ŠµŠ½ŃŒ + + Š§Š°ŃŃ‚Š¾Ń‚Š° ŠæрŠ¾Š²ŠµŃ€ŠŗŠø Š¾Š±Š½Š¾Š²Š»ŠµŠ½ŠøŠ¹: ŠŗŠ°Š¶Š“ыŠµ %u Š“Š½Ń + + Š§Š°ŃŃ‚Š¾Ń‚Š° ŠæрŠ¾Š²ŠµŃ€ŠŗŠø Š¾Š±Š½Š¾Š²Š»ŠµŠ½ŠøŠ¹: ŠŗŠ°Š¶Š“ыŠµ %u Š“Š½ŠµŠ¹ + + + + + + Stable: new minor and maintenance releases + Š”тŠ°Š±ŠøŠ»ŃŒŠ½Ń‹Šµ: Š½Š¾Š²Ń‹Šµ Š¼ŠøŠ½Š¾Ń€Š½Ń‹Šµ Šø ŠŗŠ¾Ń€Ń€ŠµŠŗтŠøрующŠøŠµ рŠµŠ»ŠøŠ·Ń‹ + + + + All releases: new minor, maintenance, and major releases + Š’сŠµ рŠµŠ»ŠøŠ·Ń‹: Š½Š¾Š²Ń‹Šµ Š¼ŠøŠ½Š¾Ń€Š½Ń‹Šµ, ŠŗŠ¾Ń€Ń€ŠµŠŗтŠøрующŠøŠµ Šø Š¼Š°Š¶Š¾Ń€Š½Ń‹Šµ рŠµŠ»ŠøŠ·Ń‹ + + + + With Betas: new minor, maintenance, major, and beta releases + Š” Š±ŠµŃ‚Š°Š¼Šø: Š½Š¾Š²Ń‹Šµ Š¼ŠøŠ½Š¾Ń€Š½Ń‹Šµ, ŠŗŠ¾Ń€Ń€ŠµŠŗтŠøрующŠøŠµ, Š¼Š°Š¶Š¾Ń€Š½Ń‹Šµ Šø Š±ŠµŃ‚Š° рŠµŠ»ŠøŠ·Ń‹ + + + + Unset + ŠŠµ Š·Š°Š“Š°Š½Š¾ + + + Update check target: %s + + Š¦ŠµŠ»ŃŒ ŠæрŠ¾Š²ŠµŃ€ŠŗŠø Š¾Š±Š½Š¾Š²Š»ŠµŠ½ŠøŠ¹: %s + + + + + Last check date: %ls + + ŠŸŠ¾ŃŠ»ŠµŠ“Š½ŃŃ Š“Š°Ń‚Š° ŠæрŠ¾Š²ŠµŃ€ŠŗŠø: %ls + + + + + User language: %ls + + ŠŸŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»ŃŒŃŠŗŠøŠ¹ яŠ·Ń‹Šŗ: %ls + + + + + Enabled: %s + + + + + + Check count: %u + + + + + + Check frequency: never + + + + + + Check frequency: every day + + + + + + Check frequency: every %u days + + + + + + + + + + Channel: %s + + + + + + Repository: %ls + + + + + + minLeaseTime: default + + Š¼ŠøŠ½. Š²Ń€ŠµŠ¼Ń Š°Ń€ŠµŠ½Š“ы: ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю + + + + + minLeaseTime: %u sec + + Š¼ŠøŠ½. Š²Ń€ŠµŠ¼Ń Š°Ń€ŠµŠ½Š“ы: %u сŠµŠŗ + + + + + defaultLeaseTime: default + + Š²Ń€ŠµŠ¼Ń Š°Ń€ŠµŠ½Š“ы ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю: ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю + + + + + defaultLeaseTime: %u sec + + Š²Ń€ŠµŠ¼Ń Š°Ń€ŠµŠ½Š“ы ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю: %u сŠµŠŗ + + + + + maxLeaseTime: default + + Š¼Š°Šŗс. Š²Ń€ŠµŠ¼Ń Š°Ń€ŠµŠ½Š“ы: ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю + + + + + maxLeaseTime: %u sec + + Š¼Š°Šŗс. Š²Ń€ŠµŠ¼Ń Š°Ń€ŠµŠ½Š“ы: %u сŠµŠŗ + + + + + Forced options: %Rhrc + + Š¤Š¾Ń€ŃŠøрŠ¾Š²Š°Š½Š½Ń‹Šµ Š¾ŠæцŠøŠø: %Rhrc + + + + + Forced options: None + + Š¤Š¾Ń€ŃŠøрŠ¾Š²Š°Š½Š½Ń‹Šµ Š¾ŠæцŠøŠø: ŠŠµŃ‚ + + + + + Forced options: + Š¤Š¾Ń€ŃŠøрŠ¾Š²Š°Š½Š½Ń‹Šµ Š¾ŠæцŠøŠø: + + + + Suppressed opt.s: %Rhrc + + ŠŸŠ¾Š“Š°Š²Š»ŠµŠ½Š½Ń‹Šµ Š¾ŠæцŠøŠø: %Rhrc + + + + + Suppressed opts.: None + + ŠŸŠ¾Š“Š°Š²Š»ŠµŠ½Š½Ń‹Šµ Š¾ŠæцŠøŠø: ŠŠµŃ‚ + + + + + Suppressed opts.: + ŠŸŠ¾Š“Š°Š²Š»ŠµŠ½Š½Ń‹Šµ Š¾ŠæцŠøŠø: + + + + DHCP options: %Rhrc + + DHCP Š¾ŠæцŠøŠø: %Rhrc + + + + + DHCP options: Return count mismatch: %zu, %zu, %zu + + DHCP Š¾ŠæцŠøŠø: Š§ŠøсŠ»Š¾ Š²Š¾Š·Š²Ń€Š°Ń‰ŠµŠ½Š½Ń‹Ń… Š½Šµ сŠ¾Š¾Ń‚Š²ŠµŃ‚стŠ²ŃƒŠµŃ‚: %zu, %zu, %zu + + + + + DHCP options: None + + DHCP Š¾ŠæцŠøŠø: ŠŠµŃ‚ + + + + + %3d/legacy: %ls + + %3d/устŠ°Ń€ŠµŠ²ŃˆŠøŠµ: %ls + + + + + NetworkName: %ls + + Š˜Š¼Ń сŠµŃ‚Šø: %ls + + + + + LowerIPAddress: %ls + + ŠŠøŠ¶Š½ŠøŠ¹ IP Š°Š“рŠµŃ: %ls + + + + + UpperIPAddress: %ls + + Š’ŠµŃ€Ń…Š½ŠøŠ¹ IP Š°Š“рŠµŃ: %ls + + + + + NetworkMask: %ls + + Š”ŠµŃ‚ŠµŠ²Š°Ń Š¼Š°ŃŠŗŠ°: %ls + + + + + Enabled: %s + + Š’ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾: %s + + + + + Global Configuration: + + Š“Š»Š¾Š±Š°Š»ŃŒŠ½Š°Ń ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†Šøя: + + + + + Groups: %Rrc + + Š“Ń€ŃƒŠæŠæы: %Rrc + + + + + Groups: None + + Š“Ń€ŃƒŠæŠæы: ŠŠµŃ‚ + + + + + Group: %ls + + Š“Ń€ŃƒŠæŠæŠ°: %ls + + + + + Conditions: %Rhrc + + Š£ŃŠ»Š¾Š²Šøя: %Rhrc + + + + + Conditions: None + + Š£ŃŠ»Š¾Š²Šøя: ŠŠµŃ‚ + + + + + Conditions: %s %s %ls + + Š£ŃŠ»Š¾Š²Šøя: %s %s %ls + + + + + include + Š²ŠŗŠ»ŃŽŃ‡Š°Ń + + + + exclude + ŠøсŠŗŠ»ŃŽŃ‡Š°Ń + + + + Individual Configs: %Rrc + + Š˜Š½Š“ŠøŠ²ŠøŠ“уŠ°Š»ŃŒŠ½Ń‹Šµ ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†ŠøŠø: %Rrc + + + + + Individual Configs: None + + Š˜Š½Š“ŠøŠ²ŠøŠ“уŠ°Š»ŃŒŠ½Ń‹Šµ ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†ŠøŠø: ŠŠµŃ‚ + + + + + Individual Config: MAC %ls + + Š˜Š½Š“ŠøŠ²ŠøŠ“уŠ°Š»ŃŒŠ½Š°Ń ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†Šøя: MAC %ls + + + + + Individual Config: VM NIC: %ls slot %u, MAC %ls + + Š˜Š½Š“ŠøŠ²ŠøŠ“уŠ°Š»ŃŒŠ½Š°Ń ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†Šøя: Š’Šœ NIC: %ls сŠ»Š¾Ń‚ %u, MAC %ls + + + + + Individual Config: VM NIC: %ls slot %u, MAC %Rhrc + + Š˜Š½Š“ŠøŠ²ŠøŠ“уŠ°Š»ŃŒŠ½Š°Ń ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†Šøя: Š’Šœ NIC: %ls сŠ»Š¾Ń‚ %u, MAC %Rhrc + + + + + Fixed Address: %ls + + Š¤ŠøŠŗсŠøрŠ¾Š²Š°Š½Š½Ń‹Š¹ Š°Š“рŠµŃ: %ls + + + + + Fixed Address: dynamic + + Š¤ŠøŠŗсŠøрŠ¾Š²Š°Š½Š½Ń‹Š¹ Š°Š“рŠµŃ: Š“ŠøŠ½Š°Š¼ŠøчŠµŃŠŗŠøŠ¹ + + + + + Extension Packs: %u + + ŠŸŠ°ŠŗŠµŃ‚Ń‹ рŠ°ŃŃˆŠøрŠµŠ½ŠøŠ¹: %u + + + + + Pack no.%2zu: %ls +Version: %ls +Revision: %u +Edition: %ls +Description: %ls +VRDE Module: %ls +Usable: %RTbool +Why unusable: %ls + + ŠŸŠ°ŠŗŠµŃ‚ no.%2zu: %ls +Š’ŠµŃ€ŃŠøя: %ls +Š ŠµŠ²ŠøŠ·Šøя: %u +Š ŠµŠ“Š°ŠŗцŠøя: %ls +ŠžŠæŠøсŠ°Š½ŠøŠµ: %ls +VRDE Š¼Š¾Š“уŠ»ŃŒ: %ls +Š“Š¾Š“Š½Ń‹Š¹: %RTbool +ŠŸŠ¾Ń‡ŠµŠ¼Ńƒ Š½Šµ Š³Š¾Š“ŠµŠ½: %ls + + + + + Video Input Devices: %u + + Š£ŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š° Š²Š²Š¾Š“Š° Š²ŠøŠ“ŠµŠ¾: %u + + + + + Supported %d screen shot formats: + + + ŠŸŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŠ¼Ń‹Š¹ %d фŠ¾Ń€Š¼Š°Ń‚ сŠ½ŠøŠ¼ŠŗŠ° эŠŗрŠ°Š½Š°: + + ŠŸŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŠ¼Ń‹Šµ %d фŠ¾Ń€Š¼Š°Ń‚Š° сŠ½ŠøŠ¼ŠŗŠ° эŠŗрŠ°Š½Š°: + + ŠŸŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŠ¼Ń‹Šµ %d фŠ¾Ń€Š¼Š°Ń‚Š¾Š² сŠ½ŠøŠ¼ŠŗŠ° эŠŗрŠ°Š½Š°: + + + + + + Supported %d cloud providers: + + + ŠŸŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŠ¼Ń‹Š¹ %d Š¾Š±Š»Š°Ń‡Š½Ń‹Š¹ ŠæрŠ¾Š²Š°Š¹Š“ŠµŃ€: + + ŠŸŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŠ¼Ń‹Šµ %d Š¾Š±Š»Š°Ń‡Š½Ń‹Ń… ŠæрŠ¾Š²Š°Š¹Š“ŠµŃ€Š°: + + ŠŸŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŠ¼Ń‹Šµ %d Š¾Š±Š»Š°Ń‡Š½Ń‹Ń… ŠæрŠ¾Š²Š°Š¹Š“ŠµŃ€Š¾Š²: + + + + + + Short Name: %ls + + ŠšŃ€Š°Ń‚ŠŗŠ¾Šµ ŠøŠ¼Ń: %ls + + + + + Name: %ls + + Š˜Š¼Ń: %ls + + + + + Provider GUID: %ls + + GUID ŠæрŠ¾Š²Š°Š¹Š“ŠµŃ€Š°: %ls + + + + + Property: + Š”Š²Š¾Š¹ŃŃ‚Š²Š¾: + + + + CPU Profile #%02zu: + + ŠŸŃ€Š¾Ń„ŠøŠ»ŃŒ Š¦ŠŸŠ£ #%02zu: + + + + + Architecture: %s + + ŠŃ€Ń…ŠøтŠµŠŗтурŠ°: %s + + + + + Name: %ls + + Š˜Š¼Ń: %ls + + + + + Full Name: %ls + + ŠŸŠ¾Š»Š½Š¾Šµ ŠøŠ¼Ń: %ls + + + + + %sDrive: %ls + + %sŠ”ŠøсŠŗ: %ls + + + + + %sDrive: %Rhrc + + %sŠ”ŠøсŠŗ: %Rhrc + + + + + Model: %Rhrc + + ŠœŠ¾Š“ŠµŠ»ŃŒ: %Rhrc + + + + + Model: "%ls" + + ŠœŠ¾Š“ŠµŠ»ŃŒ: "%ls" + + + + + Model: unknown/inaccessible + + ŠœŠ¾Š“ŠµŠ»ŃŒ: Š½ŠµŠøŠ·Š²ŠµŃŃ‚Š½Š¾/Š½ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠ½Š¾ + + + + + Further disk and partitioning information is not available for drive "%ls". (E_ACCESSDENIED) + + Š”Š°Š»ŃŒŠ½ŠµŠ¹ŃˆŠ°Ń ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†Šøя Š¾ Š“ŠøсŠŗŠµ Šø ŠµŠ³Š¾ рŠ°Š·Š“ŠµŠ»Š°Ń… Š½ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠ½Š° Š“Š»Ń Š“ŠøсŠŗŠ° "%ls". (E_ACCESSDENIED) + + + + + Size: %llu bytes (%Rhcb) + + + Š Š°Š·Š¼ŠµŃ€: %llu Š±Š°Š¹Ń‚ (%Rhcb) + + Š Š°Š·Š¼ŠµŃ€: %llu Š±Š°Š¹Ń‚Š° (%Rhcb) + + Š Š°Š·Š¼ŠµŃ€: %llu Š±Š°Š¹Ń‚ (%Rhcb) + + + + + + Size: %Rhcb + + Š Š°Š·Š¼ŠµŃ€: %Rhcb + + + + + Size: %Rhrc + + Š Š°Š·Š¼ŠµŃ€: %Rhrc + + + + + Sector Size: %u bytes + + + Š Š°Š·Š¼ŠµŃ€ сŠµŠŗтŠ¾Ń€Š°: %u Š±Š°Š¹Ń‚ + + Š Š°Š·Š¼ŠµŃ€ сŠµŠŗтŠ¾Ń€Š°: %u Š±Š°Š¹Ń‚Š° + + Š Š°Š·Š¼ŠµŃ€ сŠµŠŗтŠ¾Ń€Š°: %u Š±Š°Š¹Ń‚ + + + + + + Sector Size: %Rhrc + + Š Š°Š·Š¼ŠµŃ€ сŠµŠŗтŠ¾Ń€Š°: %Rhrc + + + + + Scheme: %s + + Š”хŠµŠ¼Š°: %s + + + + + Scheme: %Rhrc + + Š”хŠµŠ¼Š°: %Rhrc + + + + + Partitions: %Rhrc + + Š Š°Š·Š“ŠµŠ»Ń‹: %Rhrc + + + + + Partitions: None (or not able to grok them). + + Š Š°Š·Š“ŠµŠ»Ń‹: ŠŠµŃ‚ (ŠøŠ»Šø Š½ŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ рŠ°Š·Š¾Š±Ń€Š°Ń‚ŃŒŃŃ Š² Š½Šøх). + + + + + Partitions: First Last +## Type Byte Size Byte Offset Cyl/Head/Sec Cyl/Head/Sec Active + + Š Š°Š·Š“ŠµŠ»Ń‹: ŠŸŠµŃ€Š²Ń‹Š¹ ŠŸŠ¾ŃŠ»ŠµŠ“Š½ŠøŠ¹ +## Š¢ŠøŠæ Š Š°Š·Š¼ŠµŃ€ Š±Š°Š¹Ń‚ Š”Š¼ŠµŃ‰ŠµŠ½ŠøŠµ Š±Š°Š¹Ń‚ Š¦ŠøŠ»/Š“Š¾Š»/Š”ŠµŠŗт Š¦ŠøŠ»/Š“Š¾Š»/Š”ŠµŠŗт ŠŠŗтŠøŠ²Š½Ń‹Š¹ + + + + + Partitions: First Last +## Type Size Start Cyl/Head/Sec Cyl/Head/Sec Active + + Š Š°Š·Š“ŠµŠ»Ń‹: ŠŸŠµŃ€Š²Ń‹Š¹ ŠŸŠ¾ŃŠ»ŠµŠ“Š½ŠøŠ¹ +## Š¢ŠøŠæ Š Š°Š·Š¼ŠµŃ€ Š”тŠ°Ń€Ń‚ Š¦ŠøŠ»/Š“Š¾Š»/Š”ŠµŠŗт Š¦ŠøŠ»/Š“Š¾Š»/Š”ŠµŠŗт ŠŠŗтŠøŠ²Š½Ń‹Š¹ + + + + + Partitions: +## %-*s Uuid Byte Size Byte Offset Active Name + + Š Š°Š·Š“ŠµŠ»Ń‹: +## %-*s Uuid Š Š°Š·Š¼ŠµŃ€ Š±Š°Š¹Ń‚ Š”Š¼ŠµŃ‰ŠµŠ½ŠøŠµ Š±Š°Š¹Ń‚ ŠŠŗтŠøŠ²ŠµŠ½ Š˜Š¼Ń + + + + + + Type + Š¢ŠøŠæ + + + + Partitions: +## %-*s Uuid Size Start Active Name + + Š Š°Š·Š“ŠµŠ»Ń‹: +## %-*s Uuid Š Š°Š·Š¼ŠµŃ€ Š”тŠ°Ń€Ń‚ ŠŠŗтŠøŠ²ŠµŠ½ Š˜Š¼Ń + + + + + Description: %ls + + ŠžŠæŠøсŠ°Š½ŠøŠµ: %ls + + + + + Family ID: %ls + + ID сŠµŠ¼ŠµŠ¹ŃŃ‚Š²Š°: %ls + + + + + Family Desc: %ls + + ŠžŠæŠøсŠ°Š½ŠøŠµ сŠµŠ¼ŠµŠ¹ŃŃ‚Š²Š°: %ls + + + + + 64 bit: %RTbool + + 64 Š±Šøт: %RTbool + + + + + + Name: %ls + + + Š˜Š¼Ń: %ls + + + + + + Host CPUIDs: + +Leaf no. EAX EBX ECX EDX + + CPUID хŠ¾ŃŃ‚Š°: + +Š›Šøст no. EAX EBX ECX EDX + + + + + base + Š±Š°Š·Š¾Š²Ń‹Š¹ + + + Network: %ls + + Š”ŠµŃ‚ŃŒ: %ls + + + + IPv6 Enabled: %s + + IPv6 Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½: %s + + + + IPv6 Prefix: %ls + + IPv6 ŠæрŠµŃ„ŠøŠŗс: %ls + + + + DHCP Enabled: %s + + DHCP Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½: %s + + + + Port-forwarding (ipv4) + + ŠŸŠµŃ€ŠµŠ½Š°ŠæрŠ°Š²Š»ŠµŠ½ŠøŠµ ŠæŠ¾Ń€Ń‚Š¾Š² (ipv4) + + + + Port-forwarding (ipv6) + + ŠŸŠµŃ€ŠµŠ½Š°ŠæрŠ°Š²Š»ŠµŠ½ŠøŠµ ŠæŠ¾Ń€Ń‚Š¾Š² (ipv6) + + + + loopback mappings (ipv4) + + Š¾Ń‚Š¾Š±Ń€Š°Š¶ŠµŠ½ŠøŠµ Š»Š¾ŠŗŠ°Š»ŃŒŠ½Š¾Š¹ ŠæŠµŃ‚Š»Šø (ipv4) + + + + + Unknown subcommand "%s". + ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Š°Ń ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“Š° "%s". + + + + Missing subcommand for "list" command. + + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“Š° Š“Š»Ń ŠŗŠ¾Š¼Š°Š½Š“ы "list". + + + + + Metrics + + + Invalid machine name: '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ ŠøŠ¼Ń Š¼Š°ŃˆŠøŠ½Ń‹: '%s' + + + + host + хŠ¾ŃŃ‚ + + + + unknown + Š½ŠµŠøŠ·Š²ŠµŃŃ‚Š½Š¾ + + + + The following metrics were modified: + +Object Metric +---------- -------------------- + + Š”Š»ŠµŠ“ующŠøŠµ Š¼ŠµŃ‚Ń€ŠøŠŗŠø ŠøŠ·Š¼ŠµŠ½ŠµŠ½Ń‹: + +ŠžŠ±ŃŠŠµŠŗт ŠœŠµŃ‚Ń€ŠøŠŗŠ° +---------- -------------------- + + + + + No metrics match the specified filter! + ŠŠµ Š½Š°Š¹Š“ŠµŠ½Š¾ Š¼ŠµŃ‚Ń€ŠøŠŗ, ŠæŠ¾Š“хŠ¾Š“ящŠøх ŠæŠ¾Š“ уŠŗŠ°Š·Š°Š½Š½Ń‹Š¹ фŠøŠ»ŃŒŃ‚Ń€! + + + + Object Metric Unit Minimum Maximum Period Count Description +--------------- ---------------------------------------- ---- ---------- ---------- ---------- ---------- ----------- + + ŠžŠ±ŃŠŠµŠŗт ŠœŠµŃ‚Ń€ŠøŠŗŠ° Š•Š“. ŠœŠøŠ½ ŠœŠ°Šŗс ŠŸŠµŃ€ŠøŠ¾Š“ ŠšŠ¾Š»ŠøчŠµŃŃ‚Š²Š¾ ŠžŠæŠøсŠ°Š½ŠøŠµ +--------------- ---------------------------------------- ---- ---------- ---------- ---------- ---------- ----------- + + + + + + + + Missing argument to '%s' + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s' + + + + + Invalid value for 'period' parameter: '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ Š“Š»Ń ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š° 'period': '%s' + + + + + Invalid value for 'samples' parameter: '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ Š“Š»Ń ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š° 'samples': '%s' + + + + Object Metric Values +--------------- ---------------------------------------- -------------------------------------------- + + ŠžŠ±ŃŠŠµŠŗт ŠœŠµŃ‚Ń€ŠøŠŗŠ° Š—Š½Š°Ń‡ŠµŠ½Šøя +--------------- ---------------------------------------- -------------------------------------------- + + + + + The background process holding collected metrics will shutdown +in few seconds, discarding all collected data and parameters. + Š¤Š¾Š½Š¾Š²Ń‹Š¹ ŠæрŠ¾Ń†ŠµŃŃ, уŠ“ŠµŃ€Š¶ŠøŠ²Š°ŃŽŃ‰ŠøŠ¹ сŠ¾Š±Ń€Š°Š½Š½Ń‹Šµ Š¼ŠµŃ‚Ń€ŠøŠŗŠø Š·Š°Š²ŠµŃ€ŃˆŠøтся Š² +тŠµŃ‡ŠµŠ½ŠøŠµ Š½ŠµŃŠŗŠ¾Š»ŃŒŠŗŠøх сŠµŠŗуŠ½Š“, тŠµŃ€ŃŃ Š²ŃŠµ сŠ¾Š±Ń€Š°Š½Š½Ń‹Šµ Š“Š°Š½Š½Ń‹Šµ Šø +ŠæŠ°Ń€Š°Š¼ŠµŃ‚ры. + + + + Time stamp Object Metric Value + + Š’Ń€ŠµŠ¼Ń ŠžŠ±ŃŠŠµŠŗт ŠœŠµŃ‚Ń€ŠøŠŗŠ° Š—Š½Š°Ń‡ŠµŠ½ŠøŠµ + + + + + Subcommand missing + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“Š° + + + + Invalid subcommand '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š°Ń ŠæŠ¾Š“ŠŗŠ¾Š¼Š°Š½Š“Š° '%s' + + + + Misc + + + + + + + Incorrect number of parameters + ŠŠµŠŗŠ¾Ń€Ń€ŠµŠŗтŠ½Š¾Šµ ŠŗŠ¾Š»ŠøчŠµŃŃ‚Š²Š¾ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š¾Š² + + + + + Cannot convert filename "%s" to absolute path: %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæрŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Ń‚ŃŒ ŠøŠ¼Ń фŠ°Š¹Š»Š° "%s" Š² Š°Š±ŃŠ¾Š»ŃŽŃ‚Š½Ń‹Š¹ Šæуть: %Rrc + + + + + + + Invalid parameter '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ '%s' + + + + + Invalid option -%c + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š°Ń Š¾ŠæцŠøя -%c + + + + + Invalid option case %i + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š²Š°Ń€ŠøŠ°Š½Ń‚ Š¾ŠæцŠøŠø %i + + + + + unknown option: %s + + ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Š°Ń Š¾ŠæцŠøя: %s + + + + + + error: %Rrs + Š¾ŃˆŠøŠ±ŠŗŠ°: %Rrs + + + + + + VM name required + Š¢Ń€ŠµŠ±ŃƒŠµŃ‚ся ŠøŠ¼Ń Š’Šœ + + + + Machine delete failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ уŠ“Š°Š»Šøть Š¼Š°ŃˆŠøŠ½Ńƒ + + + + Parameter --name is required + Š¢Ń€ŠµŠ±ŃƒŠµŃ‚ся ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ --name + + + + Virtual machine '%ls' is created%s. +UUID: %s +Settings file: '%ls' + + Š’ŠøртуŠ°Š»ŃŒŠ½Š°Ń Š¼Š°ŃˆŠøŠ½Š° '%ls' сŠ¾Š·Š“Š°Š½Š°%s. +UUID: %s +Š¤Š°Š¹Š» Š½Š°ŃŃ‚Ń€Š¾ŠµŠŗ: '%ls' + + + + + and registered + Šø Š·Š°Ń€ŠµŠ³ŠøстрŠøрŠ¾Š²Š°Š½Š° + + + + + RTPathAbs(%s,,) failed with rc=%Rrc + RTPathAbs(%s,,) Š·Š°Š²ŠµŃ€ŃˆŠµŠ½ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹ rc=%Rrc + + + + Move VM failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠµŃ€ŠµŠ¼ŠµŃŃ‚Šøть Š’Šœ + + + + Machine has been successfully moved into %s + + ŠœŠ°ŃˆŠøŠ½Š° усŠæŠµŃˆŠ½Š¾ ŠæŠµŃ€ŠµŠ¼ŠµŃ‰ŠµŠ½Š° Š² %s + + + + + the same location + тŠ¾ Š¶Šµ Š¼ŠµŃŃ‚Š¾ + + + + Invalid clone mode '%s' + + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ рŠµŠ¶ŠøŠ¼ ŠŗŠ»Š¾Š½ŠøрŠ¾Š²Š°Š½Šøя '%s' + + + + + Invalid clone options '%s' + + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Šµ Š¾ŠæцŠøŠø ŠŗŠ»Š¾Š½ŠøрŠ¾Š²Š°Š½Šøя '%s' + + + + + %s Clone + ŠšŠ»Š¾Š½ %s + + + + Clone VM failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠŗŠ»Š¾Š½ŠøрŠ¾Š²Š°Ń‚ŃŒ Š’Šœ + + + + Machine has been successfully cloned as "%ls" + + ŠœŠ°ŃˆŠøŠ½Š° усŠæŠµŃˆŠ½Š¾ ŠŗŠ»Š¾Š½ŠøрŠ¾Š²Š°Š½Š° ŠŗŠ°Šŗ "%ls" + + + + + Parameter to option --putenv must not contain any newline character + ŠŸŠ°Ń€Š°Š¼ŠµŃ‚Ń€ Šŗ Š¾ŠæцŠøŠø --putenv Š½Šµ Š“Š¾Š»Š¶ŠµŠ½ сŠ¾Š“ŠµŃ€Š¶Š°Ń‚ŃŒ ŠæŠµŃ€ŠµŠ²Š¾Š“Š¾Š² стрŠ¾ŠŗŠø + + + + at least one VM name or uuid required + Š¢Ń€ŠµŠ±ŃƒŠµŃ‚ся хŠ¾Ń‚я Š±Ń‹ Š¾Š“Š½Š¾ ŠøŠ¼Ń Š’Šœ ŠøŠ»Šø uuid + + + + Waiting for VM "%s" to power on... + + ŠžŠ¶ŠøŠ“Š°Š½ŠøŠµ Š·Š°ŠæусŠŗŠ° Š’Šœ "%s"... + + + + + VM "%s" has been successfully started. + + Š’Šœ "%s" усŠæŠµŃˆŠ½Š¾ Š·Š°ŠæущŠµŠ½Š°. + + + + + + Key: %ls, Value: %ls + + ŠšŠ»ŃŽŃ‡: %ls, Š—Š½Š°Ń‡ŠµŠ½ŠøŠµ: %ls + + + + + + Value: %ls + + Š—Š½Š°Ń‡ŠµŠ½ŠøŠµ: %ls + + + + + + No value set! + + ŠŠµ Š·Š°Š“Š°Š½Š¾ ŠŗŠ°ŠŗŠ¾Šµ-Š»ŠøŠ±Š¾ Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ! + + + + + + Not enough parameters + ŠŠµŠ“Š¾ŃŃ‚Š°Ń‚Š¾Ń‡Š½Š¾ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š¾Š² + + + + + Too many parameters + Š”Š»ŠøшŠŗŠ¾Š¼ Š¼Š½Š¾Š³Š¾ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š¾Š² + + + + Invalid hwvirtexclusive argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ hwvirtexclusive '%s' + + + + Warning: 'vrdpauthlibrary' is deprecated. Use 'vrdeauthlibrary'. + + ŠŸŃ€ŠµŠ“уŠæрŠµŠ¶Š“ŠµŠ½ŠøŠµ: 'vrdpauthlibrary' устŠ°Ń€ŠµŠ». Š˜ŃŠæŠ¾Š»ŃŒŠ·ŃƒŠ¹Ń‚Šµ 'vrdeauthlibrary'. + + + + + Error parsing Log history count '%s' + ŠžŃˆŠøŠ±ŠŗŠ° ŠæрŠø рŠ°Š·Š±Š¾Ń€Šµ рŠ°Š·Š¼ŠµŃ€Š° ŠøстŠ¾Ń€ŠøŠø Š¶ŃƒŃ€Š½Š°Š»Š° '%s' + + + + Unknown proxy mode: '%s' + ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Ń‹Š¹ рŠµŠ¶ŠøŠ¼ ŠæрŠ¾ŠŗсŠø: '%s' + + + + + Machine name is given more than once: first '%s', then '%s' + Š˜Š¼Ń Š¼Š°ŃˆŠøŠ½Ń‹ Š“Š°Š½Š¾ Š±Š¾Š»ŠµŠµ Š¾Š“Š½Š¾Š³Š¾ рŠ°Š·Š°: сŠ½Š°Ń‡Š°Š»Š° '%s', Š·Š°Ń‚ŠµŠ¼ '%s' + + + + + No machine was specified + ŠŠµ уŠŗŠ°Š·Š°Š½Š° Š¼Š°ŃˆŠøŠ½Š° + + + + + No shared folder name (--name) was given + ŠŠµ уŠŗŠ°Š·Š°Š½Š¾ ŠøŠ¼Ń Š¾Š±Ń‰ŠµŠ¹ ŠæŠ°ŠæŠŗŠø (--name) + + + + Invalid shared folder name '%s': contains space + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ ŠøŠ¼Ń Š¾Š±Ń‰ŠµŠ¹ ŠæŠ°ŠæŠŗŠø '%s': сŠ¾Š“ŠµŃ€Š¶Šøт ŠæрŠ¾Š±ŠµŠ»Ń‹ + + + + Invalid shared folder name '%s': contains tabs + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ ŠøŠ¼Ń Š¾Š±Ń‰ŠµŠ¹ ŠæŠ°ŠæŠŗŠø '%s': сŠ¾Š“ŠµŃ€Š¶Šøт тŠ°Š±ŃƒŠ»ŃŃ†Šøю + + + + Invalid shared folder name '%s': contains newline + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ ŠøŠ¼Ń Š¾Š±Ń‰ŠµŠ¹ ŠæŠ°ŠæŠŗŠø '%s': сŠ¾Š“ŠµŃ€Š¶Šøт ŠæŠµŃ€ŠµŠ²Š¾Š“ы стрŠ¾Šŗ + + + + No host path (--hostpath) was given + ŠŠµ уŠŗŠ°Š·Š°Š½ Šæуть хŠ¾ŃŃ‚Š° (--hostpath) + + + + RTAbsPath failed on '%s': %Rrc + RTAbsPath Š·Š°Š²ŠµŃ€ŃˆŠøŠ»ŃŃ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹ Š½Š° '%s': %Rrc + + + + Machine '%s' is not currently running. + ŠœŠ°ŃˆŠøŠ½Š° '%s' сŠµŠ¹Ń‡Š°Ń Š½Šµ Š·Š°ŠæущŠµŠ½Š°. + + + + Machine '%s' is not currently running. + + ŠœŠ°ŃˆŠøŠ½Š° '%s' сŠµŠ¹Ń‡Š°Ń Š½Šµ Š·Š°ŠæущŠµŠ½Š°. + + + + + + Too many extension pack names given to "extpack uninstall" + Š”Š»ŠøшŠŗŠ¾Š¼ Š¼Š½Š¾Š³Š¾ ŠøŠ¼ŠµŠ½ ŠæŠ°ŠŗŠµŃ‚Š¾Š² рŠ°ŃŃˆŠøрŠµŠ½ŠøŠ¹ уŠŗŠ°Š·Š°Š½Š¾ Š“Š»Ń "extpack uninstall" + + + + No extension pack name was given to "extpack install" + ŠŠµ уŠŗŠ°Š·Š°Š½Š¾ ŠøŠ¼Ń ŠæŠ°ŠŗŠµŃ‚Š° рŠ°ŃŃˆŠøрŠµŠ½Šøя Š“Š»Ń "extpack install" + + + + License accepted. + + Š›ŠøцŠµŠ½Š·Šøя ŠæрŠøŠ½ŃŃ‚Š°. + + + + + Do you agree to these license terms and conditions (y/n)? + Š’Ń‹ сŠ¾Š³Š»Š°ŃŠ½Ń‹ с этŠøŠ¼Šø Š¾ŠæрŠµŠ“ŠµŠ»ŠµŠ½ŠøяŠ¼Šø Šø усŠ»Š¾Š²ŠøяŠ¼Šø Š»ŠøцŠµŠ½Š·ŠøŠø (y/n)? + + + + Installation of "%ls" aborted. + + Š£ŃŃ‚Š°Š½Š¾Š²ŠŗŠ° "%ls" ŠæрŠµŃ€Š²Š°Š½Š°. + + + + + License accepted. For batch installation add +--accept-license=%s +to the VBoxManage command line. + + + Š›ŠøцŠµŠ½Š·Šøя ŠæрŠøŠ½ŃŃ‚Š°. Š”Š»Ń ŠæŠ°ŠŗŠµŃ‚Š½Š¾Š¹ устŠ°Š½Š¾Š²ŠŗŠø Š“Š¾Š±Š°Š²ŃŒŃ‚Šµ +--accept-license=%s +Š² ŠŗŠ¾Š¼Š°Š½Š“Š½ŃƒŃŽ стрŠ¾Šŗу VBoxManage. + + + + + + Failed to install "%s" + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ устŠ°Š½Š¾Š²Šøть "%s" + + + + Successfully installed "%ls". + + Š£ŃŠæŠµŃˆŠ½Š¾ устŠ°Š½Š¾Š²Š»ŠµŠ½Š¾ "%ls". + + + + + No extension pack name was given to "extpack uninstall" + ŠŠµ уŠŗŠ°Š·Š°Š½Š¾ ŠøŠ¼Ń ŠæŠ°ŠŗŠµŃ‚Š° рŠ°ŃŃˆŠøрŠµŠ½Šøя Š“Š»Ń "extpack uninstall" + + + + Failed to uninstall "%s" + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ уŠ“Š°Š»Šøть "%s" + + + + Successfully uninstalled "%s". + + Š£ŃŠæŠµŃˆŠ½Š¾ уŠ“Š°Š»ŠµŠ½Š¾ "%s". + + + + + Successfully performed extension pack cleanup + + Š£ŃŠæŠµŃˆŠ½Š¾ ŠæрŠ¾ŠøŠ·Š²ŠµŠ“ŠµŠ½Š° Š¾Ń‡ŠøстŠŗŠ° ŠæŠ°ŠŗŠµŃ‚Š° рŠ°ŃŃˆŠøрŠµŠ½Šøя + + + + + + + + + + + RTPathAbs failed on '%s': %Rrc + RTPathAbs Š·Š°Š²ŠµŃ€ŃˆŠøŠ»ŃŃ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹ Š½Š° '%s': %Rrc + + + + No ISO specified + ŠŠµ уŠŗŠ°Š·Š°Š½ ISO + + + + Detected '%s' to be: + + ŠžŠ±Š½Š°Ń€ŃƒŠ¶ŠµŠ½Š½Ń‹Š¹ '%s': + + + + + OS TypeId = %ls + OS Version = %ls + OS Flavor = %ls + OS Languages = %ls + OS Hints = %ls + + ID тŠøŠæŠ° ŠžŠ” = %ls + Š’ŠµŃ€ŃŠøя ŠžŠ” = %ls + ŠžŃŠ¾Š±ŠµŠ½Š½Š¾ŃŃ‚ŃŒ ŠžŠ” = %ls + ŠÆŠ·Ń‹ŠŗŠø ŠžŠ” = %ls + ŠŸŠ¾Š“сŠŗŠ°Š·ŠŗŠø ŠžŠ” = %ls + + + + + VM name/UUID given more than once! + Š˜Š¼Ń Š’Šœ/UUID уŠŗŠ°Š·Š°Š½Ń‹ Š½ŠµŃŠŗŠ¾Š»ŃŒŠŗŠ¾ рŠ°Š·! + + + + Missing VM name/UUID + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ ŠøŠ¼Ń Š’Šœ/UUID + + + + Machine '%ls' is currently running + ŠœŠ°ŃˆŠøŠ½Š° '%ls' сŠµŠ¹Ń‡Š°Ń рŠ°Š±Š¾Ń‚Š°ŠµŃ‚ + + + + %s unattended installation of %s in machine '%ls' (%ls). + + %s unattended устŠ°Š½Š¾Š²ŠŗŠ° %s Š² Š¼Š°ŃˆŠøŠ½Ńƒ '%ls' (%ls). + + + + + Preparing + ŠŸŠ¾Š“Š³Š¾Ń‚Š¾Š²ŠŗŠ° + + + + Starting + Š—Š°ŠæусŠŗ + + + + Using values: + + Š˜ŃŠæŠ¾Š»ŃŒŠ·ŃƒŠµŠ¼Ń‹Šµ Š·Š½Š°Ń‡ŠµŠ½Šøя: + + + + + + + %32s = failed: %Rhrc + + %32s = Š·Š°Š²ŠµŃ€ŃˆŠµŠ½Š¾ с Š¾ŃˆŠøŠ±ŠŗŠ¾Š¹: %Rhrc + + + + + %32 = failed: %Rhrc + + + + + + VM '%ls' (%ls) is ready to be started (e.g. VBoxManage startvm). + + Š’Šœ '%ls' (%ls) Š³Š¾Ń‚Š¾Š²Š° Šŗ Š·Š°ŠæусŠŗу (Š½Š°ŠæрŠøŠ¼ŠµŃ€, VBoxManage startvm). + + + + + Waiting for VM '%ls' to power on... + + ŠžŠ¶ŠøŠ“Š°Š½ŠøŠµ Š·Š°ŠæусŠŗŠ° Š’Šœ '%ls'... + + + + + VM '%ls' (%ls) has been successfully started. + + Š’Šœ '%ls' (%ls) усŠæŠµŃˆŠ½Š¾ Š·Š°ŠæущŠµŠ½Š°. + + + + + + + + Parameter --provider is required + Š¢Ń€ŠµŠ±ŃƒŠµŃ‚ся ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ --provider + + + + + + + Parameter --profile is required + Š¢Ń€ŠµŠ±ŃƒŠµŃ‚ся ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ --profile + + + + Provider %ls: profile '%ls' was updated. + + ŠŸŃ€Š¾Š²Š°Š¹Š“ŠµŃ€ %ls: ŠæрŠ¾Ń„ŠøŠ»ŃŒ '%ls' Š¾Š±Š½Š¾Š²Š»ŠµŠ½. + + + + + Provider GUID: %ls + + GUID ŠæрŠ¾Š²Š°Š¹Š“ŠµŃ€Š°: %ls + + + + + Property: + Š”Š²Š¾Š¹ŃŃ‚Š²Š¾: + + + + Provider %ls: profile '%ls' was added. + + ŠŸŃ€Š¾Š²Š°Š¹Š“ŠµŃ€ %ls: ŠæрŠ¾Ń„ŠøŠ»ŃŒ '%ls' Š“Š¾Š±Š°Š²Š»ŠµŠ½. + + + + + Provider %ls: profile '%ls' was deleted. + + ŠŸŃ€Š¾Š²Š°Š¹Š“ŠµŃ€ %ls: ŠæрŠ¾Ń„ŠøŠ»ŃŒ '%ls' уŠ“Š°Š»ŠµŠ½. + + + + + ModifyVM + + + Warning: '--vrdp%s' is deprecated. Use '--vrde%s'. + + ŠŸŃ€ŠµŠ“уŠæрŠµŠ¶Š“ŠµŠ½ŠøŠµ: '--vrdp%s' устŠ°Ń€ŠµŠ». Š˜ŃŠæŠ¾Š»ŃŒŠ·ŃƒŠ¹Ń‚Šµ '--vrde%s'. + + + + + Invalid %s number %u + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š½Š¾Š¼ŠµŃ€ %s %u + + + + + Not enough parameters + ŠŠµŠ“Š¾ŃŃ‚Š°Ń‚Š¾Ń‡Š½Š¾ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š¾Š² + + + + Cannot open file "%s": %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š¾Ń‚Šŗрыть фŠ°Š¹Š» "%s": %Rrc + + + + Cannot get size of file "%s": %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæŠ¾Š»ŃƒŃ‡Šøть рŠ°Š·Š¼ŠµŃ€ фŠ°Š¹Š»Š° "%s": %Rrc + + + + File "%s" is bigger than 256KByte + Š¤Š°Š¹Š» "%s" Š±Š¾Š»ŃŒŃˆŠµ 256 ŠšŠ‘Š°Š¹Ń‚ + + + + Cannot read contents of file "%s": %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæрŠ¾Ń‡ŠµŃŃ‚ŃŒ сŠ¾Š“ŠµŃ€Š¶ŠøŠ¼Š¾Šµ фŠ°Š¹Š»Š° "%s": %Rrc + + + + Invalid --firmware argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --firmware '%s' + + + + Invalid --paravirtprovider argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --paravirtprovider '%s' + + + Missing or Invalid argument to '%s' + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ ŠøŠ»Šø Š½ŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s' + + + + + + + + + Missing or invalid argument to '%s' + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ ŠøŠ»Šø Š½ŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s' + + + + Invalid --graphicscontroller argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --graphicscontroller '%s' + + + + Invalid --biosbootmenu argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --biosbootmenu '%s' + + + + Invalid --biosapic argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --biosapic '%s' + + + + Invalid boot device '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ Š·Š°Š³Ń€ŃƒŠ·Š¾Ń‡Š½Š¾Šµ устрŠ¾Š¹ŃŃ‚Š²Š¾ '%s' + + + + Invalid --idecontroller argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --idecontroller '%s' + + + + Invalid --usb argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --usb '%s' + + + + Invalid --scsitype argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --scsitype '%s' + + + + + Invalid host DVD drive name "%s" + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ ŠøŠ¼Ń DVD Š“ŠøсŠŗŠ¾Š²Š¾Š“Š° хŠ¾ŃŃ‚Š° "%s" + + + + Invalid host floppy drive name "%s" + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ ŠøŠ¼Ń фŠ»Š¾ŠæŠæŠø Š“ŠøсŠŗŠ¾Š²Š¾Š“Š° хŠ¾ŃŃ‚Š° "%s" + + + + Invalid --nicproperty%d argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --nicproperty%d '%s' + + + + Error: Failed to allocate memory for --nicproperty%d '%s' + + ŠžŃˆŠøŠ±ŠŗŠ°: ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š²Ń‹Š“ŠµŠ»Šøть ŠæŠ°Š¼ŃŃ‚ŃŒ Š“Š»Ń --nicproperty%d '%s' + + + + + Invalid NIC type '%s' specified for NIC %u + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ тŠøŠæ NIC '%s', уŠŗŠ°Š·Š°Š½Š½Ń‹Š¹ Š“Š»Ń NIC %u + + + + Invalid boot priority '%u' specfied for NIC %u + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹ ŠæрŠøŠ¾Ń€ŠøтŠµŃ‚ Š·Š°Š³Ń€ŃƒŠ·ŠŗŠø '%u' уŠŗŠ°Š·Š°Š½Š½Ń‹Š¹ Š“Š»Ń NIC %u + + + + Unknown promiscuous mode policy '%s' + ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Š°Ń ŠæŠ¾Š»ŠøтŠøŠŗŠ° Š½ŠµŃ€Š°Š·Š±Š¾Ń€Ń‡ŠøŠ²Š¾Š³Š¾ рŠµŠ¶ŠøŠ¼Š° '%s' + + + + Invalid type '%s' specfied for NIC %u + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ тŠøŠæ '%s', уŠŗŠ°Š·Š°Š½Š½Ń‹Š¹ Š“Š»Ń NIC %u + + + + Invalid proto '%s' specfied for NIC %u + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠæрŠ¾Ń‚Š¾ŠŗŠ¾Š» '%s', уŠŗŠ°Š·Š°Š½Š½Ń‹Š¹ Š“Š»Ń NIC %u + + + + Invalid type '%s' specfied for pointing device + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ тŠøŠæ '%s', уŠŗŠ°Š·Š°Š½Š½Ń‹Š¹ Š“Š»Ń уŠŗŠ°Š·Š°Ń‚ŠµŠ»ŃŒŠ½Š¾Š³Š¾ устрŠ¾Š¹ŃŃ‚Š²Š° + + + + Invalid type '%s' specfied for keyboard + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ тŠøŠæ '%s', уŠŗŠ°Š·Š°Š½Š½Ń‹Š¹ Š“Š»Ń ŠŗŠ»Š°Š²ŠøŠ°Ń‚ŃƒŃ€Ń‹ + + + + Invalid argument to '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s' + + + + Error parsing UART I/O base '%s' + ŠžŃˆŠøŠ±ŠŗŠ° ŠæрŠø рŠ°Š·Š±Š¾Ń€Šµ Š±Š°Š·Š¾Š²Š¾Š³Š¾ Š°Š“рŠµŃŠ° I/O UART '%s' + + + + Error parsing LPT I/O base '%s' + ŠžŃˆŠøŠ±ŠŗŠ° ŠæрŠø рŠ°Š·Š±Š¾Ń€Šµ Š±Š°Š·Š¾Š²Š¾Š³Š¾ Š°Š“рŠµŃŠ° I/O LPT '%s' + + + + Invalid --audiocontroller argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --audiocontroller '%s' + + + + Invalid --audiocodec argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --audiocodec '%s' + + + + Invalid --audio argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --audio '%s' + + + + Invalid --clipboard-mode argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --clipboard-mode '%s' + + + + Invalid --clipboard-file-transfers argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --clipboard-file-transfers '%s' + + + + Invalid --draganddrop argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --draganddrop '%s' + + + + Invalid --vrdeproperty argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --vrdeproperty '%s' + + + + Error: Failed to allocate memory for VRDE property '%s' + + ŠžŃˆŠøŠ±ŠŗŠ°: ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š²Ń‹Š“ŠµŠ»Šøть ŠæŠ°Š¼ŃŃ‚ŃŒ ŠæŠ¾Š“ сŠ²Š¾Š¹ŃŃ‚Š²Š¾ VRDE '%s' + + + + + Invalid --vrdeauthtype argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --vrdeauthtype '%s' + + + + Invalid --usbrename parameters, nothing renamed + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Šµ ŠæŠ°Ń€Š°Š¼ŠµŃ‚ры Šŗ --usbrename, Š½ŠøчŠµŠ³Š¾ Š½Šµ ŠæŠµŃ€ŠµŠøŠ¼ŠµŠ½Š¾Š²Š°Š½Š¾ + + + + *** I/O APIC must be enabled for ICH9, enabling. *** + + *** I/O APIC Š“Š¾Š»Š¶ŠµŠ½ Š±Ń‹Ń‚ŃŒ Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½ Š“Š»Ń ICH9, Š²ŠŗŠ»ŃŽŃ‡ŠµŠ½ŠøŠµ. *** + + + + + Invalid --chipset argument '%s' (valid: piix3,ich9) + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --chipset '%s' (Š“Š¾ŠæустŠøŠ¼Ń‹Šµ: piix3,ich9) + + + + Invalid --iommu argument '%s' (valid: none,amd,automatic) + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --iommu '%s' (Š“Š¾ŠæустŠøŠ¼Ń‹Šµ: none,amd,automatic) + + + + Warning: On Intel hosts, 'automatic' will not enable an IOMMU since the Intel IOMMU device is not supported yet. + + ŠŸŃ€ŠµŠ“уŠæрŠµŠ¶Š“ŠµŠ½ŠøŠµ: ŠŠ° хŠ¾ŃŃ‚Š°Ń… Intel, 'automatic' Š½Šµ Š²ŠŗŠ»ŃŽŃ‡Š°ŠµŃ‚ IOMMU, тŠ°Šŗ ŠŗŠ°Šŗ Intel IOMMU устрŠ¾Š¹ŃŃ‚Š²Š° ŠµŃ‰Šµ Š½Šµ ŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŃŽŃ‚ся. + + + + + Invalid --iommu argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --iommu '%s' + + + + Invalid --tpm-type argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --tpm-type '%s' + + + + Invalid list of screens specified + + Š£ŠŗŠ°Š·Š°Š½ Š½ŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ сŠæŠøсŠ¾Šŗ эŠŗрŠ°Š½Š¾Š² + + + + + Cannot convert filename "%s" to absolute path + + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæрŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Ń‚ŃŒ ŠøŠ¼Ń фŠ°Š¹Š»Š° "%s" Š² Š°Š±ŃŠ¾Š»ŃŽŃ‚Š½Ń‹Š¹ Šæуть + + + + + + Error parsing video resolution '%s' (expected <width>x<height>) + ŠžŃˆŠøŠ±ŠŗŠ° рŠ°Š·Š±Š¾Ń€Š° Š²ŠøŠ“ŠµŠ¾ рŠ°Š·Ń€ŠµŃˆŠµŠ½Šøя '%s' (Š¾Š¶ŠøŠ“Š°ŠµŃ‚ся <шŠøрŠøŠ½Š°>x<Š²Ń‹ŃŠ¾Ń‚Š°>) + + + + Invalid --autostop-type argument '%s' (valid: disabled, savestate, poweroff, acpishutdown) + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --autostop-type '%s' (Š“Š¾ŠæустŠøŠ¼Ń‹Šµ: disabled, savestate, poweroff, acpishutdown) + + + + Invalid --pciattach argument '%s' (valid: 'HB:HD.HF@GB:GD.GF' or just 'HB:HD.HF') + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --pciattach '%s' (Š“Š¾ŠæустŠøŠ¼Ń‹Šµ: 'HB:HD.HF@GB:GD.GF' ŠøŠ»Šø ŠæрŠ¾ŃŃ‚Š¾ 'HB:HD.HF') + + + + Invalid --pcidetach argument '%s' (valid: 'HB:HD.HF') + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --pcidetach '%s' (Š“Š¾ŠæустŠøŠ¼Ń‹Š¹: 'HB:HD.HF') + + + + Invalid --vm-process-priority '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ --vm-process-priority '%s' + + + + --testing-cfg-dword index %u is out of range: 0 thru 9 + Š˜Š½Š“ŠµŠŗс --testing-cfg-dword %u Š²Š½Šµ Š³Ń€Š°Š½Šøц Š“ŠøŠ°ŠæŠ°Š·Š¾Š½Š°: 0 - 9 + + + + Nat + + + Name: %ls + + Š˜Š¼Ń: %ls + + + + + Network: %ls + + Š”ŠµŃ‚ŃŒ: %ls + + + + + Gateway: %ls + + ŠØŠ»ŃŽŠ·: %ls + + + + DHCP Sever: %s + + DHCP сŠµŃ€Š²ŠµŃ€: %s + + + + + + + + Yes + Š”Š° + + + + + + + No + ŠŠµŃ‚ + + + + IPv6 Prefix: %ls + + IPv6 ŠæрŠµŃ„ŠøŠŗс: %ls + + + + + IPv6 Default: %s + + IPv6 ŠæŠ¾ уŠ¼Š¾Š»Ń‡Š°Š½Šøю: %ls + + + + + Enabled: %s + + Š’ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾: %s + + + + + DHCP Server: %s + + + + + + Port-forwarding (ipv4) + + ŠŸŠµŃ€ŠµŠ½Š°ŠæрŠ°Š²Š»ŠµŠ½ŠøŠµ ŠæŠ¾Ń€Ń‚Š¾Š² (ipv4) + + + + + Port-forwarding (ipv6) + + ŠŸŠµŃ€ŠµŠ½Š°ŠæрŠ°Š²Š»ŠµŠ½ŠøŠµ ŠæŠ¾Ń€Ń‚Š¾Š² (ipv6) + + + + + loopback mappings (ipv4) + + Š¾Ń‚Š¾Š±Ń€Š°Š¶ŠµŠ½ŠøŠµ Š»Š¾ŠŗŠ°Š»ŃŒŠ½Š¾Š¹ ŠæŠµŃ‚Š»Šø (ipv4) + + + + + NAT Networks: + + + Š”ŠµŃ‚Šø NAT: + + + + + + %zu %s found + + ŠŠ°Š¹Š“ŠµŠ½Š¾ %zu %s + + + + + network + сŠµŃ‚ŃŒ + + + + networks + + сŠµŃ‚ŃŒ + сŠµŃ‚Šø + сŠµŃ‚ŠµŠ¹ + + + + + + Not enough parameters + ŠŠµŠ“Š¾ŃŃ‚Š°Ń‚Š¾Ń‡Š½Š¾ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š¾Š² + + + + You can only specify --netname only once. + --netname Š¼Š¾Š¶Š½Š¾ уŠŗŠ°Š·Š°Ń‚ŃŒ тŠ¾Š»ŃŒŠŗŠ¾ Š¾Š“ŠøŠ½ рŠ°Š·. + + + + You can only specify --network only once. + --network Š¼Š¾Š¶Š½Š¾ уŠŗŠ°Š·Š°Ń‚ŃŒ тŠ¾Š»ŃŒŠŗŠ¾ Š¾Š“ŠøŠ½ рŠ°Š·. + + + + + You can specify either --enable or --disable once. + --enable Šø --disable Š¼Š¾Š¶Š½Š¾ уŠŗŠ°Š·Š°Ń‚ŃŒ тŠ¾Š»ŃŒŠŗŠ¾ Š¾Š“ŠøŠ½ рŠ°Š·. + + + + You can specify --dhcp only once. + --dhcp Š¼Š¾Š¶Š½Š¾ уŠŗŠ°Š·Š°Ń‚ŃŒ тŠ¾Š»ŃŒŠŗŠ¾ Š¾Š“ŠøŠ½ рŠ°Š·. + + + + You can specify --ipv6 only once. + --ipv6 Š¼Š¾Š¶Š½Š¾ уŠŗŠ°Š·Š°Ń‚ŃŒ тŠ¾Š»ŃŒŠŗŠ¾ Š¾Š“ŠøŠ½ рŠ°Š·. + + + + You can specify --ipv6-prefix only once. + --ipv6-prefix Š¼Š¾Š¶Š½Š¾ уŠŗŠ°Š·Š°Ń‚ŃŒ тŠ¾Š»ŃŒŠŗŠ¾ Š¾Š“ŠøŠ½ рŠ°Š·. + + + + You can specify --ipv6-default only once. + + + + + loopback couldn't be deleted on modified + + Š›Š¾ŠŗŠ°Š»ŃŒŠ½Š°Ń ŠæŠµŃ‚Š»Ń Š½Šµ Š¼Š¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ уŠ“Š°Š»ŠµŠ½Š° ŠøŠ»Šø ŠøŠ·Š¼ŠµŠ½ŠµŠ½Š° + + + + + + Not enough parŠ°meters + + ŠŠµŠ“Š¾ŃŃ‚Š°Ń‚Š¾Ń‡Š½Š¾ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š¾Š² + + + + + Invalid port-forward rule %s + + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ ŠæрŠ°Š²ŠøŠ»Š¾ ŠæŠµŃ€ŠµŠ½Š°ŠæрŠ°Š²Š»ŠµŠ½Šøя ŠæŠ¾Ń€Ń‚Š¾Š² %s + + + + + Port-forward could be deleted on modify + + ŠŸŃ€Š°Š²ŠøŠ»Š¾ ŠæŠµŃ€ŠµŠ½Š°ŠæрŠ°Š²Š»ŠµŠ½Šøя ŠæŠ¾Ń€Ń‚Š¾Š² Š¼Š¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ уŠ“Š°Š»ŠµŠ½Š¾ ŠæрŠø ŠøŠ·Š¼ŠµŠ½ŠµŠ½ŠøŠø + + + + + Port-forward rule name is too long + + Š˜Š¼Ń ŠæрŠ°Š²ŠøŠ»Š° ŠæŠµŃ€ŠµŠ½Š°ŠæрŠ°Š²Š»ŠµŠ½Šøя ŠæŠ¾Ń€Ń‚Š¾Š² сŠ»ŠøшŠŗŠ¾Š¼ Š±Š¾Š»ŃŒŃˆŠ¾Šµ + + + + + You need to specify the --netname option + ŠŠµŠ¾Š±Ń…Š¾Š“ŠøŠ¼Š¾ уŠŗŠ°Š·Š°Ń‚ŃŒ Š¾ŠæцŠøю --netname + + + + You need to specify the --network option + ŠŠµŠ¾Š±Ń…Š¾Š“ŠøŠ¼Š¾ уŠŗŠ°Š·Š°Ń‚ŃŒ Š¾ŠæцŠøю --network + + + + Unknown operation (:%d) + ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Š°Ń Š¾ŠæŠµŃ€Š°Ń†Šøя (:%d) + + + + NATNetwork server already exists + Š”ŠµŃ€Š²ŠµŃ€ сŠµŃ‚Šø NAT уŠ¶Šµ сущŠµŃŃ‚Š²ŃƒŠµŃ‚ + + + + Failed to create the NAT network service + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Š·Š“Š°Ń‚ŃŒ сŠ»ŃƒŠ¶Š±Ńƒ сŠµŃ‚Šø NAT + + + + NATNetwork server does not exist + Š”ŠµŃ€Š²ŠµŃ€ сŠµŃ‚Šø NAT Š½Šµ сущŠµŃŃ‚Š²ŃƒŠµŃ‚ + + + + + + + + + + Failed to set configuration + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°Š“Š°Ń‚ŃŒ ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†Šøю + + + + Failed to delete pf + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ уŠ“Š°Š»Šøть ŠæрŠ°Š²ŠøŠ»Š¾ ŠæŠµŃ€ŠµŠ½Š°ŠæрŠ°Š²Š»ŠµŠ½Šøя ŠæŠ¾Ń€Ń‚Š¾Š² + + + + Failed to add pf + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š“Š¾Š±Š°Š²Šøть ŠæрŠ°Š²ŠøŠ»Š¾ ŠæŠµŃ€ŠµŠ½Š°ŠæрŠ°Š²Š»ŠµŠ½Šøя ŠæŠ¾Ń€Ń‚Š¾Š² + + + + + invalid loopback string + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š°Ń стрŠ¾ŠŗŠ° Š»Š¾ŠŗŠ°Š»ŃŒŠ½Š¾Š¹ ŠæŠµŃ‚Š»Šø + + + + Failed to remove nat network + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ уŠ“Š°Š»Šøть сŠµŃ‚ŃŒ NAT + + + + Failed to start network + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°ŠæустŠøть сŠµŃ‚ŃŒ + + + + Failed to stop network + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š¾ŃŃ‚Š°Š½Š¾Š²Šøть сŠµŃ‚ŃŒ + + + + Invalid parameter '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ '%s' + + + + Nvram + + + No platform key file path was given to "enrollpk" + ŠŠµ уŠŗŠ°Š·Š°Š½ Šæуть Šŗ фŠ°Š¹Š»Ńƒ с ŠŗŠ»ŃŽŃ‡Š¾Š¼ ŠæŠ»Š°Ń‚Ń„Š¾Ń€Š¼Ń‹ Š“Š»Ń "enrollpk" + + + + No owner UUID was given to "enrollpk" + ŠŠµ уŠŗŠ°Š·Š°Š½ UUID Š²Š»Š°Š“ŠµŠ»ŃŒŃ†Š° Š“Š»Ń "enrollpk" + + + + Cannot read contents of file "%s": %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæрŠ¾Ń‡ŠµŃŃ‚ŃŒ сŠ¾Š“ŠµŃ€Š¶ŠøŠ¼Š¾Šµ фŠ°Š¹Š»Š° "%s": %Rrc + + + + File "%s" is bigger than 32KByte + Š¤Š°Š¹Š» "%s" Š±Š¾Š»ŃŒŃˆŠµ 32 ŠšŠ‘Š°Š¹Ń‚ + + + + Cannot get size of file "%s": %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ ŠæŠ¾Š»ŃƒŃ‡Šøть рŠ°Š·Š¼ŠµŃ€ фŠ°Š¹Š»Š° "%s": %Rrc + + + + Cannot open file "%s": %Rrc + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š¾Ń‚Šŗрыть фŠ°Š¹Š» "%s": %Rrc + + + + No variable name was given to "queryvar" + ŠŠµ уŠŗŠ°Š·Š°Š½Š¾ ŠøŠ¼Ń ŠæŠµŃ€ŠµŠ¼ŠµŠ½Š½Š¾Š¹ Š“Š»Ń "queryvar" + + + + Error writing to '%s': %Rrc + ŠžŃˆŠøŠ±ŠŗŠ° ŠæрŠø Š·Š°ŠæŠøсŠø Š² '%s': %Rrc + + + + + Error opening '%s': %Rrc + ŠžŃˆŠøŠ±ŠŗŠ° ŠæрŠø Š¾Ń‚ŠŗрытŠøŠø '%s': %Rrc + + + + No variable name was given to "deletevar" + ŠŠµ уŠŗŠ°Š·Š°Š½Š¾ ŠøŠ¼Ń ŠæŠµŃ€ŠµŠ¼ŠµŠ½Š½Š¾Š¹ Š“Š»Ń "deletevar" + + + + No owner UUID was given to "deletevar" + ŠŠµ уŠŗŠ°Š·Š°Š½ UUID Š²Š»Š°Š“ŠµŠ»ŃŒŃ†Š° Š“Š»Ń "deletevar" + + + + No variable name was given to "changevar" + ŠŠµ уŠŗŠ°Š·Š°Š½Š¾ ŠøŠ¼Ń ŠæŠµŃ€ŠµŠ¼ŠµŠ½Š½Š¾Š¹ Š“Š»Ń "changevar" + + + + No variable data filename was given to "changevar" + ŠŠµ уŠŗŠ°Š·Š°Š½Š¾ ŠøŠ¼Ń фŠ°Š¹Š»Š° с Š“Š°Š½Š½Ń‹Š¼Šø Š“Š»Ń "changevar" + + + + Error reading from '%s': %Rrc + ŠžŃˆŠøŠ±ŠŗŠ° ŠæрŠø чтŠµŠ½ŠøŠø ŠøŠ· '%s': %Rrc + + + + Snapshot + + + + + This machine does not have any snapshots + + Š£ этŠ¾Š¹ Š¼Š°ŃˆŠøŠ½Ń‹ Š½ŠµŃ‚ сŠ½ŠøŠ¼ŠŗŠ¾Š² + + + + + [%RI32] Images and snapshots for medium "%ls" + + [%RI32] ŠžŠ±Ń€Š°Š·Ń‹ Šø сŠ½ŠøŠ¼ŠŗŠø Š½Š¾ŃŠøтŠµŠ»Ń "%ls" + + + + + Not enough parameters + ŠŠµŠ“Š¾ŃŃ‚Š°Ń‚Š¾Ń‡Š½Š¾ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š¾Š² + + + + + Missing snapshot name + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ ŠøŠ¼Ń сŠ½ŠøŠ¼ŠŗŠ° + + + + Invalid unique name description '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ Š¾ŠæŠøсŠ°Š½ŠøŠµ уŠ½ŠøŠŗŠ°Š»ŃŒŠ½Š¾Š³Š¾ ŠøŠ¼ŠµŠ½Šø '%s' + + + + Failed to generate a unique snapshot name + ŠŠµ уŠ“Š°Š»Š¾ŃŃ‚ сŠ³ŠµŠ½ŠµŃ€ŠøрŠ¾Š²Š°Ń‚ŃŒ уŠ½ŠøŠŗŠ°Š»ŃŒŠ½Š¾Šµ ŠøŠ¼Ń сŠ½ŠøŠ¼ŠŗŠ° + + + + Snapshot taken. UUID: %ls + + Š”Š½ŠøŠ¼Š¾Šŗ сŠ“ŠµŠ»Š°Š½. UUID: %ls + + + + + Failed to take snapshot + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ“ŠµŠ»Š°Ń‚ŃŒ сŠ½ŠøŠ¼Š¾Šŗ + + + + Too many arguments + Š”Š»ŠøшŠŗŠ¾Š¼ Š¼Š½Š¾Š³Š¾ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚Š¾Š² + + + + + Expecting snapshot name only + ŠžŠ¶ŠøŠ“Š°ŠµŃ‚ся тŠ¾Š»ŃŒŠŗŠ¾ ŠøŠ¼Ń сŠ½ŠøŠ¼ŠŗŠ° + + + + %s snapshot '%ls' (%ls) + + %s сŠ½ŠøŠ¼ŠŗŠ° '%ls' (%ls) + + + + + Deleting + Š£Š“Š°Š»ŠµŠ½ŠøŠµ + + + + Restoring + Š’Š¾ŃŃŃ‚Š°Š½Š¾Š²Š»ŠµŠ½ŠøŠµ + + + + Snapshot operation failed + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾Š²ŠµŃŃ‚Šø Š¾ŠæŠµŃ€Š°Ń†Šøю Š½Š°Š“ сŠ½ŠøŠ¼ŠŗŠ¾Š¼ + + + + Invalid parameter '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ '%s' + + + + Storage + + + + Invalid --type argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --type '%s' + + + + Invalid medium type '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ тŠøŠæ Š½Š¾ŃŠøтŠµŠ»Ń '%s' + + + + Storage controller name not specified + ŠŠµ уŠŗŠ°Š·Š°Š½Š¾ ŠøŠ¼Ń ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€Š° Š½Š¾ŃŠøтŠµŠ»ŠµŠ¹ + + + + Drive passthrough state cannot be changed while the VM is running + + Š”Š¾ŃŃ‚Š¾ŃŠ½ŠøŠµ ŠæряŠ¼Š¾Š³Š¾ Š“Š¾ŃŃ‚ŃƒŠæŠ° Šŗ Š“ŠøсŠŗу Š½Šµ Š¼Š¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ ŠøŠ·Š¼ŠµŠ½ŠµŠ½Š¾ ŠæŠ¾ŠŗŠ° рŠ°Š±Š¾Ń‚Š°ŠµŃ‚ Š’Šœ + + + + + Bandwidth group cannot be changed while the VM is running + + Š“Ń€ŃƒŠæŠæ ŠæŠ¾Š»Š¾ŃŃ‹ ŠæрŠ¾ŠæусŠŗŠ°Š½Šøя Š½Šµ Š¼Š¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ ŠøŠ·Š¼ŠµŠ½ŠµŠ½Š° ŠæŠ¾ŠŗŠ° рŠ°Š±Š¾Ń‚Š°ŠµŃ‚ Š’Šœ + + + + + Could not find a controller named '%s' + + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š½Š°Š¹Ń‚Šø ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€ с ŠøŠ¼ŠµŠ½ŠµŠ¼ '%s' + + + + + Port not specified + ŠŸŠ¾Ń€Ń‚ Š½Šµ уŠŗŠ°Š·Š°Š½ + + + + Device not specified + Š£ŃŃ‚Ń€Š¾Š¹ŃŃ‚Š²Š¾ Š½Šµ уŠŗŠ°Š·Š°Š½Š¾ + + + + No DVD/Floppy Drive attached to the controller '%s'at the port: %u, device: %u + ŠŠ° ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€ '%s' ŠæŠ¾Ń€Ń‚ %u устрŠ¾Š¹ŃŃ‚Š²Š¾: %u Š½Šµ ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾ ŠŗŠ°ŠŗŠøх Š»ŠøŠ±Š¾ DVD ŠøŠ»Šø Š¤Š»Š¾ŠæŠæŠø Š“ŠøсŠŗŠ¾Š² + + + + The attachment is not supported by the storage controller '%s' + ŠŸŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ŠøŠµ Š“Š°Š½Š½Š¾Š³Š¾ устрŠ¾Š¹ŃŃ‚Š²Š° Š½Šµ ŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŃ‚ся ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€Š¾Š¼ '%s' + + + + Cannot find the Guest Additions ISO image + + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š½Š°Š¹Ń‚Šø ISO Š¾Š±Ń€Š°Š· Š”Š¾ŠæŠ¾Š»Š½ŠµŠ½ŠøŠ¹ Š“Š¾ŃŃ‚ŠµŠ²Š¾Š¹ ŠžŠ” + + + + + Argument --type must be specified + + ŠŃ€Š³ŃƒŠ¼ŠµŠ½Ń‚ --type Š“Š¾Š»Š¶ŠµŠ½ Š±Ń‹Ń‚ŃŒ уŠŗŠ°Š·Š°Š½ + + + + + The given attachment is not supported by the storage controller '%s' + Š£ŠŗŠ°Š·Š°Š½Š½Š¾Šµ ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ŠøŠµ устрŠ¾Š¹ŃŃ‚Š²Š° Š½Šµ ŠæŠ¾Š“Š“ŠµŃ€Š¶ŠøŠ²Š°ŠµŃ‚ся ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€Š¾Š¼ Š½Š¾ŃŠøтŠµŠ»ŠµŠ¹ '%s' + + + + + Invalid host DVD drive name "%s" + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ ŠøŠ¼Ń DVD Š“ŠøсŠŗŠ¾Š²Š¾Š“Š° хŠ¾ŃŃ‚Š° "%s" + + + + Invalid host floppy drive name "%s" + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ ŠøŠ¼Ń фŠ»Š¾ŠæŠæŠø Š“ŠøсŠŗŠ¾Š²Š¾Š“Š° хŠ¾ŃŃ‚Š° "%s" + + + + Parameters --server and --target are required for iSCSI media + Š”Š»Ń iSCSI Š½Š¾ŃŠøтŠµŠ»ŠµŠ¹ трŠµŠ±ŃƒŃŽŃ‚ся ŠæŠ°Ń€Š°Š¼ŠµŃ‚ры --server Šø --target + + + + iSCSI disk created. UUID: %s + + Š”Š¾Š·Š“Š°Š½ iSCSI Š“ŠøсŠŗ. UUID: %s + + + + + Missing --medium argument + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ --medium + + + + Invalid UUID or filename "%s" + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ UUID ŠøŠ»Šø ŠøŠ¼Ń фŠ°Š¹Š»Š° "%s" + + + + Failed to set the medium/parent medium UUID + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°Š“Š°Ń‚ŃŒ UUID Š½Š¾ŃŠøтŠµŠ»Ń ŠøŠ»Šø UUID рŠ¾Š“ŠøтŠµŠ»ŃŒŃŠŗŠ¾Š³Š¾ Š½Š¾ŃŠøтŠµŠ»Ń + + + + Failed to set the medium type + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ Š·Š°Š“Š°Ń‚ŃŒ тŠøŠæ Š½Š¾ŃŠøтŠµŠ»Ń + + + + Invalid --passthrough argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --passthrough '%s' + + + + + + + + Couldn't find the controller attachment for the controller '%s' + + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š½Š°Š¹Ń‚Šø ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½ŠøŠµ Šŗ ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€Ńƒ '%s' + + + + + Invalid --tempeject argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --tempeject '%s' + + + + Invalid --nonrotational argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --nonrotational '%s' + + + + Invalid --discard argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --discard '%s' + + + + Invalid --hotpluggable argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --hotpluggable '%s' + + + + Too few parameters + Š”Š»ŠøшŠŗŠ¾Š¼ Š¼Š°Š»Š¾ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š¾Š² + + + + Storage controller name not specified + + ŠŠµ уŠŗŠ°Š·Š°Š½Š¾ ŠøŠ¼Ń ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€Š° Š½Š¾ŃŠøтŠµŠ»ŠµŠ¹ + + + + + Invalid --add argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --add '%s' + + + + + + + + Couldn't find the controller with the name: '%s' + + ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ Š½Š°Š¹Ń‚Šø ŠŗŠ¾Š½Ń‚Ń€Š¾Š»Š»ŠµŃ€ с ŠøŠ¼ŠµŠ½ŠµŠ¼ '%s' + + + + + Invalid --hostiocache argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --hostiocache '%s' + + + + Invalid --bootable argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --bootable '%s' + + + + UpdateCheck + + + Enabled: %s + + Š’ŠŗŠ»ŃŽŃ‡ŠµŠ½Š¾: %s + + + + + yes + Š“Š° + + + + no + Š½ŠµŃ‚ + + + + Count: %u + + ŠšŠ¾Š»ŠøчŠµŃŃ‚Š²Š¾: %u + + + + + Frequency: never + + Š§Š°ŃŃ‚Š¾Ń‚Š°: Š½ŠøŠŗŠ¾Š³Š“Š° + + + + + Frequency: every day + + Š§Š°ŃŃ‚Š¾Ń‚Š°: ŠŗŠ°Š¶Š“ыŠ¹ Š“ŠµŠ½ŃŒ + + + + + Frequency: every %u days + + Š§Š°ŃŃ‚Š¾Ń‚Š°: ŠŗŠ°Š¶Š“ыŠ¹ %u Š“ŠµŠ½ŃŒ + + + + + Stable - new minor and maintenance releases + Š”тŠ°Š±ŠøŠ»ŃŒŠ½Ń‹Šµ - Š½Š¾Š²Ń‹Šµ Š¼ŠøŠ½Š¾Ń€Š½Ń‹Šµ Šø ŠŗŠ¾Ń€Ń€ŠµŠŗтŠøрующŠøŠµ рŠµŠ»ŠøŠ·Ń‹ + + + + All releases - new minor, maintenance, and major releases + Š’сŠµ рŠµŠ»ŠøŠ·Ń‹ - Š½Š¾Š²Ń‹Šµ Š¼ŠøŠ½Š¾Ń€Š½Ń‹Šµ, ŠŗŠ¾Ń€Ń€ŠµŠŗтŠøрующŠøŠµ Šø Š¼Š°Š¶Š¾Ń€Š½Ń‹Šµ рŠµŠ»ŠøŠ·Ń‹ + + + + With Betas - new minor, maintenance, major, and beta releases + Š” Š±ŠµŃ‚Š°Š¼Šø - Š½Š¾Š²Ń‹Šµ Š¼ŠøŠ½Š¾Ń€Š½Ń‹Šµ, ŠŗŠ¾Ń€Ń€ŠµŠŗтŠøрующŠøŠµ, Š¼Š°Š¶Š¾Ń€Š½Ń‹Šµ Šø Š±ŠµŃ‚Š° рŠµŠ»ŠøŠ·Ń‹ + + + + Unset + ŠŠµ Š·Š°Š“Š°Š½Š¾ + + + + Channel: %s + + + + + + Unknown channel specified: '%s' + + + + + Checking for a new %ls version... + + + + + + Failed to create update progress object: %Rhrc + + + + + + Checking for update failed. + + + + + A new version of %ls has been released! Version %ls is available at virtualbox.org. +You can download this version here: %ls + + + + + + You are already running the most recent version of %ls. + + + + + + Something went wrong while checking for updates! +Please check network connection and try again later. + + + + + Target: %s + + Š¦ŠµŠ»ŃŒ: %s + + + + + Last Check Date: %ls + + ŠŸŠ¾ŃŠ»ŠµŠ“Š½ŃŃ Š“Š°Ń‚Š° ŠæрŠ¾Š²ŠµŃ€ŠŗŠø: %ls + + + + Unknown target specified: '%s' + Š£ŠŗŠ°Š·Š°Š½Š° Š½ŠµŠøŠ·Š²ŠµŃŃ‚Š½Š°Ń цŠµŠ»ŃŒ: '%s' + + + + The update frequency cannot be zero + Š§Š°ŃŃ‚Š¾Ń‚Š° Š¾Š±Š½Š¾Š²Š»ŠµŠ½ŠøŠ¹ Š½Šµ Š¼Š¾Š¶ŠµŃ‚ Š±Ń‹Ń‚ŃŒ Š½ŃƒŠ»ŠµŠ²Š¾Š¹ + + + + No change requested + ŠŠµ Š·Š°ŠæрŠ¾ŃˆŠµŠ½Š¾ ŠøŠ·Š¼ŠµŠ½ŠµŠ½ŠøŠ¹ + + + Checking for a new VirtualBox version... + + ŠŸŃ€Š¾Š²ŠµŃ€ŠŗŠ° Š½Š° Š½Š°Š»ŠøчŠøŠµ Š½Š¾Š²Š¾Š¹ Š²ŠµŃ€ŃŠøŠø VirtualBox... + + + + VirtualBox update checking has been disabled. + + ŠŸŃ€Š¾Š²ŠµŃ€ŠŗŠ° Š¾Š±Š½Š¾Š²Š»ŠµŠ½ŠøŠ¹ VirtualBox Š¾Ń‚ŠŗŠ»ŃŽŃ‡ŠµŠ½Š°. + + + + Failed to create ptrProgress object: %Rhrc + + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Š·Š“Š°Ń‚ŃŒ Š¾Š±ŃŠŠµŠŗт ptrProgress: %Rhrc + + + + Check for update failed. + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠ¾Š²ŠµŃ€Šøть Š¾Š±Š½Š¾Š²Š»ŠµŠ½Šøя. + + + A new version of VirtualBox has been released! Version %ls is available at virtualbox.org. +You can download this version here: %ls + + Š’Ń‹ŠæущŠµŠ½Š° Š½Š¾Š²Š°Ń Š²ŠµŃ€ŃŠøя VirtualBox! Š’ŠµŃ€ŃŠøя %ls Š“Š¾ŃŃ‚ŃƒŠæŠ½Š° Š½Š° сŠ°Š¹Ń‚Šµ virtualbox.org. +Š’Ń‹ Š¼Š¾Š¶ŠµŃ‚Šµ Š·Š°Š³Ń€ŃƒŠ·Šøть эту Š²ŠµŃ€ŃŠøю Š·Š“ŠµŃŃŒ: %ls + + + + You are already running the most recent version of VirtualBox. + + Š’Ń‹ уŠ¶Šµ ŠøсŠæŠ¾Š»ŃŒŠ·ŃƒŠµŃ‚Šµ ŠæŠ¾ŃŠ»ŠµŠ“Š½ŃŽŃŽ Š²ŠµŃ€ŃŠøю VirtualBox. + + + + + Usb + + + + + + + Not enough parameters + ŠŠµŠ“Š¾ŃŃ‚Š°Ń‚Š¾Ń‡Š½Š¾ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š¾Š² + + + + Invalid parameter '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ '%s' + + + + Invalid index '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠøŠ½Š“ŠµŠŗс '%s' + + + + + + + + + + + + + + + + Missing argument to '%s' + ŠžŃ‚ŃŃƒŃ‚ŃŃ‚Š²ŃƒŠµŃ‚ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ '%s' + + + + Invalid --active argument '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ Š°Ń€Š³ŃƒŠ¼ŠµŠ½Ń‚ Šŗ --active '%s' + + + + Failed to convert the --maskedinterfaces value '%s' to a number, vrc=%Rrc + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæрŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š°Ń‚ŃŒ Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ --maskedinterfaces '%s' Š² чŠøсŠ»Š¾, vrc=%Rrc + + + + Invalid USB filter action '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ Š“ŠµŠ¹ŃŃ‚Š²ŠøŠµ USB фŠøŠ»ŃŒŃ‚Ń€Š° '%s' + + + + Unknown option '%s' + ŠŠµŠøŠ·Š²ŠµŃŃ‚Š½Š°Ń Š¾ŠæцŠøя '%s' + + + + + Mandatory options not supplied + ŠŠµ ŠæрŠµŠ“Š¾ŃŃ‚Š°Š²Š»ŠµŠ½Ń‹ Š¾Š±ŃŠ·Š°Ń‚ŠµŠ»ŃŒŠ½Ń‹Šµ Š¾ŠæцŠøŠø + + + + Invalid number of parameters + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š¾Šµ ŠŗŠ¾Š»ŠøчŠµŃŃ‚Š²Š¾ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€Š¾Š² + + + + Parameter "%s" is invalid + ŠŠµŠ“Š¾ŠæустŠøŠ¼Ń‹Š¹ ŠæŠ°Ń€Š°Š¼ŠµŃ‚Ń€ '%s' + + + + Utils + + + type bridged + тŠøŠæŠ° сŠµŃ‚ŠµŠ²Š¾Š¹ Š¼Š¾ŃŃ‚ + + + + type host-only + тŠøŠæŠ° Š²ŠøртуŠ°Š»ŃŒŠ½Š°Ń сŠµŃ‚ŃŒ хŠ¾ŃŃ‚Š° + + + + unknown type %RU32 + Š½ŠµŠøŠ·Š²ŠµŃŃ‚Š½Š¾Š³Š¾ тŠøŠæŠ° %RU32 + + + + Interface "%s" is of %s + Š˜Š½Ń‚ŠµŃ€Ń„ŠµŠ¹Ń "%s" %s + + + + Interface "%s" doesn't seem to exist + ŠšŠ°Š¶ŠµŃ‚ся, ŠøŠ½Ń‚ŠµŃ€Ń„ŠµŠ¹Ń "%s" Š½Šµ сущŠµŃŃ‚Š²ŃƒŠµŃ‚ + + + + VBoxManage + + + + Progress object failure: %Rhrc + + ŠžŃ‚ŠŗŠ°Š· Š¾Š±ŃŠŠµŠŗтŠ° ŠæрŠ¾Š³Ń€ŠµŃŃŠ°: %Rhrc + + + + + Failed to get progress description: %Rhrc + + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠ¾Š»ŃƒŃ‡Šøть Š¾ŠæŠøсŠ°Š½ŠøŠµ ŠæрŠ¾Š³Ń€ŠµŃŃŠ°: %Rhrc + + + + + (%u/%u) %ls %02u%% => %02u%% (%d s remaining) + + (%u/%u) %ls %02u%% => %02u%% (%d сŠµŠŗ Š¾ŃŃ‚Š°Š»Š¾ŃŃŒ) + + + + + CANCELED + + ŠžŠ¢ŠœŠ•ŠŠ•ŠŠž + + + + + Progress state: %Rhrc + + Š”Š¾ŃŃ‚Š¾ŃŠ½ŠøŠµ ŠæрŠ¾Š³Ń€ŠµŃŃŠ°: %Rhrc + + + + + Password expected + ŠžŠ¶ŠøŠ“Š°ŠµŃ‚ся ŠæŠ°Ń€Š¾Š»ŃŒ + + + + No password file specified + ŠŠµ уŠŗŠ°Š·Š°Š½ фŠ°Š¹Š» с ŠæŠ°Ń€Š¾Š»ŠµŠ¼ + + + + Only one response file allowed + Š Š°Š·Ń€ŠµŃˆŠ°ŠµŃ‚ся тŠ¾Š»ŃŒŠŗŠ¾ Š¾Š“ŠøŠ½ фŠ°Š¹Š» Š¾Ń‚Š²ŠµŃ‚Š¾Š² + + + + Error reading response file '%s': %Rrc + ŠžŃˆŠøŠ±ŠŗŠ° чтŠµŠ½Šøя фŠ°Š¹Š»Š° Š¾Ń‚Š²ŠµŃ‚Š¾Š² '%s': %Rrc + + + + Invalid response file ('%s') encoding: %Rrc + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š°Ń ŠŗŠ¾Š“ŠøрŠ¾Š²ŠŗŠ° фŠ°Š¹Š»Š° Š¾Ń‚Š²ŠµŃ‚Š¾Š² ('%s'): %Rrc + + + + Failed to parse response file '%s' (bourne shell style): %Rrc + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ рŠ°Š·Š¾Š±Ń€Š°Ń‚ŃŒ фŠ°Š¹Š» Š¾Ń‚Š²ŠµŃ‚Š¾Š² '%s' (стŠøŠ»ŃŒ bourne shell): %Rrc + + + + out of memory + Š½Šµ хŠ²Š°Ń‚Š°ŠµŃ‚ ŠæŠ°Š¼ŃŃ‚Šø + + + + commands: + + ŠŗŠ¾Š¼Š°Š½Š“ы: + + + + + Invalid command '%s' + ŠŠµŠ“Š¾ŠæустŠøŠ¼Š°Ń ŠŗŠ¾Š¼Š°Š½Š“Š° '%s' + + + + Failed to initialize COM because the global settings directory '%s' is not accessible! + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠøŠ½ŠøцŠøŠ°Š»ŠøŠ·ŠøрŠ¾Š²Š°Ń‚ŃŒ COM, ŠæŠ¾Ń‚Š¾Š¼Ńƒ чтŠ¾ Š“ŠøрŠµŠŗтŠ¾Ń€Šøя Š³Š»Š¾Š±Š°Š»ŃŒŠ½Ń‹Ń… Š½Š°ŃŃ‚Ń€Š¾ŠµŠŗ '%s' Š½ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠ½Š°! + + + + Failed to initialize COM! (hrc=%Rhrc) + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠøŠ½ŠøцŠøŠ°Š»ŠøŠ·ŠøрŠ¾Š²Š°Ń‚ŃŒ COM! (hrc=%Rhrc) + + + + Failed to create a session object! + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Š·Š“Š°Ń‚ŃŒ Š¾Š±ŃŠŠµŠŗт сŠµŃŃŠøŠø! + + + + Failed to create the VirtualBox object! + ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ сŠ¾Š·Š“Š°Ń‚ŃŒ Š¾Š±ŃŠŠµŠŗт VirtualBox! + + + + Most likely, the VirtualBox COM server is not running or failed to start. + ŠŠ°ŠøŠ±Š¾Š»ŠµŠµ Š²ŠµŃ€Š¾ŃŃ‚Š½Š¾, чтŠ¾ COM сŠµŃ€Š²ŠµŃ€ VirtualBox Š½Šµ Š·Š°ŠæущŠµŠ½ ŠøŠ»Šø Š½Šµ сŠ¼Š¾Š³ Š·Š°ŠæустŠøться. + + + diff --git a/src/VBox/Frontends/VBoxManage/nls/VBoxManageNls_xx_YY.ts b/src/VBox/Frontends/VBoxManage/nls/VBoxManageNls_xx_YY.ts new file mode 100644 index 00000000..d3e72f7b --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/nls/VBoxManageNls_xx_YY.ts @@ -0,0 +1,10640 @@ + + + + + Appliance + + + + Option "%s" can't be used together with "--cloud" option. + + + + + + + + Value of option "%s" is out of range. + + + + + + + + + + + Option "%s" requires preceding --vsys or --cloud option. + + + + + + + + + + + + + + + + + + + + + Option "%s" requires preceding --vsys option. + + + + + + + + + Option "%s" requires preceding --unit option. + + + + + Invalid import options '%s' + + + + + + + Option "%s" can't be used together with "--vsys" option. + + + + + + + + + + + + + + + + + + + + Option "%s" requires preceding --cloud option. + + + + + Invalid parameter '%s' + + + + + Invalid option -%c + + + + + Invalid option case %i + + + + + unknown option: %s + + + + + + error: %Rrs + + + + + Not enough arguments for "import" command. + + + + + Not enough arguments for import from the Cloud. + + + + + Appliance read failed + + + + + Interpreting %ls... + + + + + + Disks: + + + + + + Invalid index %RI32 with -vsys option; the OVF contains only %zu virtual system(s). + + + + + + + Virtual system %u: + + + + + + %2u: OS type specified with --ostype: "%ls" + + + + + + %2u: Suggested OS type: "%ls" + (change with "--vsys %u --ostype <type>"; use "list ostypes" to list all possible values) + + + + + + %2u: VM name specified with --vmname: "%ls" + + + + + + %2u: Suggested VM name "%ls" + (change with "--vsys %u --vmname <name>") + + + + + + %2u: Product (ignored): %ls + + + + + + %2u: ProductUrl (ignored): %ls + + + + + + %2u: Vendor (ignored): %ls + + + + + + %2u: VendorUrl (ignored): %ls + + + + + + %2u: Version (ignored): %ls + + + + + + %2u: Description specified with --description: "%ls" + + + + + + %2u: Description "%ls" + (change with "--vsys %u --description <desc>") + + + + + + %2u: End-user license agreement + (accept with "--vsys %u --eula accept"): + +%ls + + + + + + + %2u: End-user license agreement (accepted) + + + + + + Argument to --eula must be either "show" or "accept". + + + + + %2u: End-user license agreement + (display with "--vsys %u --eula show"; + accept with "--vsys %u --eula accept") + + + + + + %2u: No. of CPUs specified with --cpus: %ls + + + + + + Argument to --cpus option must be a number greater than %d and less than %d. + + + + + %2u: Number of CPUs: %ls + (change with "--vsys %u --cpus <n>") + + + + + + %2u: Guest memory specified with --memory: %ls MB + + + + + + Argument to --memory option must be a non-negative number. + + + + + %2u: Guest memory: %ls MB + (change with "--vsys %u --memory <MB>") + + + + + + %2u: IDE controller, type %ls -- disabled + + + + + + %2u: IDE controller, type %ls + (disable with "--vsys %u --unit %u --ignore") + + + + + + %2u: SATA controller, type %ls -- disabled + + + + + + %2u: SATA controller, type %ls + (disable with "--vsys %u --unit %u --ignore") + + + + + + %2u: SAS controller, type %ls -- disabled + + + + + + %2u: SAS controller, type %ls + (disable with "--vsys %u --unit %u --ignore") + + + + + + %2u: SCSI controller, type %ls -- disabled + + + + + + %2u: SCSI controller, type set with --unit %u --scsitype: "%ls" + + + + + + %2u: SCSI controller, type %ls + (change with "--vsys %u --unit %u --scsitype {BusLogic|LsiLogic}"; + disable with "--vsys %u --unit %u --ignore") + + + + + + %2u: VirtioSCSI controller, type %ls -- disabled + + + + + + %2u: VirtioSCSI controller, type %ls + (disable with "--vsys %u --unit %u --ignore") + + + + + + %2u: Hard disk image: source image=%ls -- disabled + + + + + + Option --ImportToVDI can not be used together with a manually set target path. + + + + + Invalid controller value: '%s' + + + + + + Invalid storage controller specified: %u + + + + + Invalid port value: '%s' + + + + + Failed to extract controller value from ExtraConfig: '%s' + + + + + Failed to extract channel value from ExtraConfig: '%s' + + + + + Device already attached to controller %u at this port (%u) location. + + + + + Illegal port value: %u. For %ls controllers the only valid values are 0 to %lu (inclusive) + + + + + %2u: Hard disk image specified with --disk: source image=%ls, target path=%ls, %s + (change controller with "--vsys %u --unit %u --controller <index>"; + change controller port with "--vsys %u --unit %u --port <n>") + + + + + + %2u: Hard disk image specified with --disk and --controller: source image=%ls, target path=%ls, %s + (change controller port with "--vsys %u --unit %u --port <n>") + + + + + + %2u: Hard disk image specified with --disk and --port: source image=%ls, target path=%ls, %s + (change controller with "--vsys %u --unit %u --controller <index>") + + + + + + %2u: Hard disk image specified with --controller and --port: source image=%ls, target path=%ls, %s + (change target path with "--vsys %u --unit %u --disk path") + + + + + + %2u: Hard disk image specified with --port: source image=%ls, target path=%ls, %s + (change target path with "--vsys %u --unit %u --disk path"; + change controller with "--vsys %u --unit %u --controller <index>") + + + + + + %2u: Hard disk image specified with --controller: source image=%ls, target path=%ls, %s + (change target path with "--vsys %u --unit %u --disk path"; + change controller port with "--vsys %u --unit %u --port <n>") + + + + + + %2u: Hard disk image specified with --disk and --controller and --port: source image=%ls, target path=%ls, %s + + + + + + %2u: Hard disk image: source image=%ls, target path=%ls, %s + (change target path with "--vsys %u --unit %u --disk path"; + change controller with "--vsys %u --unit %u --controller <index>"; + change controller port with "--vsys %u --unit %u --port <n>"; + disable with "--vsys %u --unit %u --ignore") + + + + + + %2u: CD-ROM -- disabled + + + + + + %2u: CD-ROM + (disable with "--vsys %u --unit %u --ignore") + + + + + + %2u: Floppy -- disabled + + + + + + %2u: Floppy + (disable with "--vsys %u --unit %u --ignore") + + + + + + %2u: Network adapter: orig %ls, config %ls, extra %ls + + + + + + %2u: USB controller -- disabled + + + + + + %2u: USB controller + (disable with "--vsys %u --unit %u --ignore") + + + + + + %2u: Sound card "%ls" -- disabled + + + + + + %2u: Sound card (appliance expects "%ls", can change on import) + (disable with "--vsys %u --unit %u --ignore") + + + + + + %2u: VM settings file name specified with --settingsfile: "%ls" + + + + + + %2u: Suggested VM settings file name "%ls" + (change with "--vsys %u --settingsfile <filename>") + + + + + + %2u: VM base folder specified with --basefolder: "%ls" + + + + + + %2u: Suggested VM base folder "%ls" + (change with "--vsys %u --basefolder <path>") + + + + + + %2u: VM group specified with --group: "%ls" + + + + + + %2u: Suggested VM group "%ls" + (change with "--vsys %u --group <group>") + + + + + + %2u: Suggested cloud shape "%ls" + + + + + + %2u: Cloud bucket id specified with --cloudbucket: "%ls" + + + + + + %2u: Suggested cloud bucket id "%ls" + (change with "--cloud %u --cloudbucket <id>") + + + + + + %2u: Cloud profile name specified with --cloudprofile: "%ls" + + + + + + %2u: Suggested cloud profile name "%ls" + (change with "--cloud %u --cloudprofile <id>") + + + + + + %2u: Cloud instance id specified with --cloudinstanceid: "%ls" + + + + + + %2u: Suggested cloud instance id "%ls" + (change with "--cloud %u --cloudinstanceid <id>") + + + + + + %2u: Suggested cloud base image id "%ls" + + + + + + Cannot import until the license agreement listed above is accepted. + + + + + Cannot import until the %c license agreements listed above are accepted. + + + + + Appliance import failed + + + + + Successfully imported the appliance. + + + + + + You can only specify --output once. + + + + + Invalid export options '%s' + + + + + + unhandled option: -%c + + + + + unhandled option: %i + + + + + unknown option: %s + + + + + At least one machine must be specified with the export command. + + + + + Missing --output argument with export command. + + + + + Invalid index %RI32 with -vsys option; you specified only %zu virtual system(s). + + + + + + + Cannot read license file "%s" which should be included in the virtual system %u. + + + + + Enter the passwords for the following identifiers to export the apppliance: + + + + + + Password ID %s: + + + + + Appliance write failed + + + + + Successfully exported %d machine(s). + + + + + + + + Creating a cloud instance... + + + + + + Creating the cloud instance failed + + + + + A cloud instance with id '%s' (provider '%s') was created + + + + + + Failed to open OVA '%s' for updating: %Rrc + + + + + Failed to open OVA '%s' as a TAR file: %Rrc + + + + + Scanning OVA '%s' for a manifest and signature... + + + + + RTVfsFsStrmNext returned %Rrc + + + + + OVA contains multiple manifests! first: %s second: %s + + + + + + Unsupported OVA file ordering! Signature file ('%s') as succeeded by '%s'. + + + + + Found manifest file: %s + + + + + Failed to memorize the manifest: %Rrc + + + + + + + + Out of memory! + + + + + Multiple signature files! (%s) + + + + + Found existing signature file: %s + + + + + The OVA contains no manifest and cannot be signed! + + + + + The OVA is already signed ('%s')! (Use the --force option to force re-signing it.) + + + + + Writing '%s' to the OVA... + + + + + RTZipTarFsStreamTruncate failed on '%s': %Rrc + + + + + RTVfsFileSeek(hVfsFileSignature) failed: %Rrc + + + + + RTVfsFsStrmAdd('%s') failed on '%s': %Rrc + + + + + RTVfsFsStrmEnd failed on '%s': %Rrc + + + + + Successfully decoded the PKCS#7/CMS signature... + + + + + Successfully verified the PKCS#7/CMS signature + + + + + Failed to verify the PKCS#7/CMS signature: %Rrc%RTeim + + + + + RTCrPkcs7SignedData_CheckSanity failed on PKCS#7/CMS signature: %Rrc%RTeim + + + + + PKCS#7/CMS signature inner ContentType isn't 'data' but: %s + + + + + PKCS#7/CMD signature is not 'signedData': %s + + + + + RTCrPkcs7ContentInfo_Clone failed: %Rrc + + + + + RTCrPkcs7ContentInfo_DecodeAsn1 failed to decode PKCS#7/CMS signature: %Rrc%RTemi + + + + + Manifest is too big: %#RX64 bytes, max 4MiB + + + + + + + RTCrStoreCertAddFromFile failed on '%s': %Rrc%#RTeim + + + + + RTCrStoreCreateInMem failed: %Rrc + + + + + Created PKCS#7/CMS signature: %zu bytes, %s. + + + + + + + Using SHA-1 instead of SHA-3 for the PKCS#7/CMS signature. + + + + + RTCrPemWriteBlobToVfsFile failed: %Rrc + + + + + RTCrPkcs7SimpleSignSignedData failed: %Rrc%#RTeim + + + + + RTVfsFileReadAt failed: %Rrc + + + + + Unsupported digest type: %s + + + + + Failed to create digest for %s: %Rrc + + + + + Created OVA signature: %zu bytes, %s + + + + + + + Successfully decoded and verified the OVA signature. + + + + + + Failed to write certificate to signature file: %Rrc%#RTeim + + + + + Failed to produce signature file: %Rrc + + + + + RTVfsMemFileCreate failed: %Rrc + + + + + Encountered a problem when validating the signature we just created: %Rrc%#RTeim +Please make sure the certificate and private key matches. + + + + + 2nd RTCrPkixPubKeySignDigest call failed: %Rrc%#RTeim + + + + + RTCrPkixPubKeySignDigest failed: %Rrc%#RTeim + + + + + Failed to create digest %s: %Rrc + + + + + + Password is given more than once. + + + + + Unknown digest type: %s + + + + + Too many intermediate certificates: max %zu + + + + + No OVA file was specified! + + + + + No signing certificate (--certificate=<file>) was specified! + + + + + No signing private key (--private-key=<file>) was specified! + + + + + The specified OVA file was not found: %s + + + + + The specified certificate file was not found: %s + + + + + The specified private key file was not found: %s + + + + + Error reading certificate from '%s': %Rrc%#RTeim + + + + + Successfully read the certificate and private key. + + + + + Successfully signed '%s'. + + + + + Error reading the private key from %s: %Rrc%#RTeim + + + + + BWControl + + + + Limit is too big + + + + + + Invalid unit suffix. Valid suffixes are: k, m, g, K, M, G + + + + + + Trailing spaces in limit! + + + + + + No digits in limit specifier + + + + + + Invalid limit specifier + + + + + + Limit cannot be negative + + + + + + Bandwidth group name must not be empty! + + + + + + Invalid bandwidth group type + + + + + + Too few parameters + + + + + Too many parameters + + + + + Bandwidth groups cannot be created while the VM is running + + + + + + Bandwidth groups cannot be deleted while the VM is running + + + + + + Invalid parameter '%s' + + + + + Cloud + + + Parameter --provider is required + + + + + Parameter --profile is required + + + + + Unknown cloud instance state "%s" + + + + + + Parameter 'compartment' is empty or absent. +Trying to get the compartment from the passed cloud profile '%s' + + + + + + + Found the compartment '%s': + + + + + + + Parameter --compartment-id is required + + + + + Reply is in the form 'instance name' = 'instance id' + + + + + + Failed to list instances + + + + + The list of the instances for the cloud profile '%ls' +and compartment '%s': + + + + + + Unknown cloud image state "%s" + + + + + Reply is in the form 'image name' = 'image id' + + + + + + Failed to list images + + + + + The list of the images for the cloud profile '%ls' +and compartment '%s': + + + + + + + + + + + + + + + + + + Empty command parameter list, show help. + + + + + + Warning!!! Public SSH key doesn't present in the passed arguments... + + + + + + Parameters --image-id and --boot-volume-id are mutually exclusive. Only one of them must be presented. + + + + + Missing parameter --image-id or --boot-volume-id. One of them must be presented. + + + + + Checking the cloud image with id '%s'... + + + + + + Checking the cloud image failed + + + + + Creating cloud instance with name '%s' from the image '%s'... + + + + + + Creating cloud instance with name '%s' from the boot volume '%s'... + + + + + + Creating cloud instance failed + + + + + Cloud instance was created successfully + + + + + + + + + + + Duplicate parameter: --id + + + + + + + + + + Empty parameter: --id + + + + + + + + + + Missing parameter: --id + + + + + Getting information about cloud instance with id %s... + + + + + + Reply is in the form 'setting name' = 'value' + + + + + + Getting information about cloud instance failed + + + + + Cloud instance info (provider '%s'): + + + + + + Availability domain = %ls + + + + + + Availability domain wasn't found + + + + + + Instance displayed name = %ls + + + + + + Instance displayed name wasn't found + + + + + + Instance state = %ls + + + + + + Instance state wasn't found + + + + + + Instance Id = %ls + + + + + + Instance Id wasn't found + + + + + + Instance name = %ls + + + + + + Instance name wasn't found + + + + + + Bootable image Id = %ls + + + + + + Image Id whom the instance is booted up wasn't found + + + + + + Shape of the instance = %ls + + + + + + The shape of the instance wasn't found + + + + + + Type of guest OS = %ls + + + + + + Type of guest OS wasn't found + + + + + + RAM = %ls MB + + + + + + Value for RAM wasn't found + + + + + + CPUs = %ls + + + + + + Numbers of CPUs weren't found + + + + + + Instance public IP = %ls + + + + + + Public IP wasn't found + + + + + + Free-form tags or metadata weren't found + + + + + + Cloud-init script wasn't found + + + + + + Starting cloud instance with id %s... + + + + + + Starting the cloud instance failed + + + + + Cloud instance with id %s (provider = '%s', profile = '%s') was started + + + + + + Pausing cloud instance with id %s... + + + + + + Pause the cloud instance failed + + + + + Cloud instance with id %s (provider = '%s', profile = '%s') was paused + + + + + + Terminating cloud instance with id %s... + + + + + + Termination the cloud instance failed + + + + + Cloud instance with id %s (provider = '%s', profile = '%s') was terminated + + + + + + Conflicting parameters: --instance-id and --object-name can't be used together. Choose one. + + + + + Creating cloud image with name '%s' from the instance '%s'... + + + + + + Creating cloud image with name '%s' from the object '%s' in the bucket '%s'... + + + + + + Creating cloud image failed + + + + + Cloud image was created successfully + + + + + + Duplicate parameter: --bucket-name + + + + + Empty parameter: --bucket-name + + + + + Duplicate parameter: --object-name + + + + + Empty parameter: --object-name + + + + + Duplicate parameter: --display-name + + + + + Empty parameter: --display-name + + + + + Duplicate parameter: --launch-mode + + + + + Empty parameter: --launch-mode + + + + + Missing parameter: --bucket-name + + + + + Exporting image '%s' to the Cloud with name '%s'... + + + + + + Exporting image '%s' to the Cloud with default name + + + + + + Image %s was found + + + + + + Process of exporting the image to the Cloud was interrupted. The image wasn't found. + + + + + + Export the image to the Cloud failed + + + + + Export the image to the Cloud was successfull + + + + + + Creating an object '%s' from the cloud image '%s'... + + + + + + Cloud image import failed + + + + + Cloud image was imported successfully. Find the downloaded object with the name %s in the system temp folder (find the possible environment variables like TEMP, TMP and etc.) + + + + + + Getting information about the cloud image with id '%s'... + + + + + + Reply is in the form 'image property' = 'value' + + + + + + Getting information about the cloud image failed + + + + + General information about the image: + + + + + + Deleting cloud image with id %s... + + + + + + Deleting cloud image failed + + + + + Cloud image was deleted successfully + + + + + + + + + Missing --name parameter + + + + + Missing --network-id parameter + + + + + Cloud network was created successfully + + + + + + Name: %ls + + + + + + State: %s + + + + + + Enabled + + + + + Disabled + + + + + CloudProvider: %ls + + + + + + CloudProfile: %ls + + + + + + CloudNetworkId: %ls + + + + + + VBoxNetworkName: %ls + + + + + + + Cloud network %ls was updated successfully + + + + + + Cloud network %ls was deleted successfully + + + + + + Setting up tunnel network in the cloud... + + + + + + Setting up cloud network environment failed + + + + + Cloud network environment was set up successfully. Tunnel network id is: %ls + + + + + + CloudMachine + + + cloud: no providers available + + + + + cloud: multiple providers available, '--provider' option is required + + + + + cloud: no profiles exist + + + + + cloud: multiple profiles exist, '--profile' option is required + + + + + ambiguous name: %ls and %ls + + + + + only one machine can be specified + + + + + not a valid uuid: %s + + + + + machine not specified + + + + + machine name is empty + + + + + unable to find machine with id %s + + + + + unable to find machine with name %s + + + + + unable to find machine %s + + + + + cloud machine: RTGetOptInit: %Rra + + + + + + Invalid sub-command: %s + + + + + cloud machine: internal error: %d + + + + + cloud machine: command required +Try '--help' for more information. + + + + + cloud machine list: unexpected machine argument + + + + + cloud machine list: RTGetOptInit: %Rra + + + + + machine is not accessible + + + + + State: Invalid (%RU32) + + + + + + State: Provisioning (%RU32) + + + + + + State: Running (%RU32) + + + + + + State: Starting (%RU32) + + + + + + State: Stopping (%RU32) + + + + + + State: Stopped (%RU32) + + + + + + State: CreatingImage (%RU32) + + + + + + State: Terminating (%RU32) + + + + + + State: Terminated (%RU32) + + + + + + State: Unknown state (%RU32) + + + + + + null details + + + + + %ls: unable to convert to boolean value + + + + + + %ls: unable to convert to string value + + + + + + %ls: unable to convert to integer value + + + + + + %ls: unable to convert to choice value + + + + + + %ls: values: %Rhra + + + + + %ls: selectedIndex: %Rhra + + + + + %ls: selected index %RI64 out of range [0, %zu) + + + + + + unknown value type %RU32 + + + + + + ControlVM + + + Invalid %s number '%s'. + + + + + Failed to send a scancode. + + + + + Out of memory allocating %d bytes. + + + + + + + File size %RI64 is greater than %RI64: '%s'. + + + + + Cannot get size of file '%s': %Rrc. + + + + + Cannot open file '%s': %Rrc. + + + + + + Not enough parameters. + + + + + Machine '%s' is not currently running. + + + + + + + + + + + + + + + + + + + + + + + + + + + + Missing argument to '%s'. + + + + + + + Invalid value '%s'. + + + + + + Audio adapter not enabled in VM configuration. + + + + + + Missing argument to '%s %s'. + + + + + + Invalid '%s %s' argument '%s'. + + + + + + Invalid '%s' argument '%s'. + + + + + Failed to power off machine. + + + + + Machine in invalid state %d -- %s. + + + + + Failed to save machine state. + + + + + + + + + + + + + + Guest not running. + + + + + Current installed Guest Additions don't support rebooting the guest. + + + + + Current installed Guest Additions don't support shutting down the guest. + + + + + Missing argument to '%s'. Expected IBM PC AT set 2 keyboard scancode(s). + + + + + Converting '%s' returned %Rrc! + + + + + '%s' is not a hex byte! + + + + + Missing argument to '%s'. Expected ASCII string(s). + + + + + Invalid link state '%s'. + + + + + Filename not specified for NIC %lu. + + + + + The NIC %d is currently disabled and thus its tracefile can't be changed. + + + + + Invalid nictrace%lu argument '%s'. + + + + + The NIC %d is currently disabled and thus its trace flag can't be changed. + + + + + Missing or invalid argument to '%s'. + + + + + Wrong rule proto '%s' specified -- only 'udp' and 'tcp' are allowed. + + + + + Invalid nicproperty%d argument '%s'. + + + + + Failed to allocate memory for nicproperty%d '%s'. + + + + + The NIC %d is currently disabled and thus its properties can't be changed. + + + + + Unknown promiscuous mode policy '%s'. + + + + + The NIC %d is currently disabled and thus its promiscuous mode can't be changed. + + + + + Invalid type '%s' specfied for NIC %lu. + + + + + The NIC %d is currently disabled and thus its attachment type can't be changed. + + + + + 'vrdp' is deprecated. Use 'vrde'. + + + + + Invalid remote desktop server state '%s'. + + + + + 'vrdpport' is deprecated. Use 'vrdeport'. + + + + + 'vrdpvideochannelquality' is deprecated. Use 'vrdevideochannelquality'. + + + + + Invalid vrdeproperty argument '%s'. + + + + + Failed to allocate memory for VRDE property '%s'. + + + + + Wrong number of arguments. + + + + + + + Invalid parameter '%s'. + + + + + Zero UUID argument '%s'. + + + + + + + + + + + + + + + + + + + + + Incorrect number of parameters. + + + + + Either "yes" or "no" is expected. + + + + + Display status must be <on> or <off>. + + + + + Error parsing guest memory balloon size '%s'. + + + + + Teleportation failed + + + + + Error parsing display number '%s'. + + + + + Failed to create file '%s' (%Rrc). + + + + + Failed to write screenshot to file '%s' (%Rrc). + + + + + Error parsing list of screen IDs '%s'. + + + + + Error parsing video width '%s'. + + + + + Error parsing video height '%s'. + + + + + Error parsing video rate '%s'. + + + + + Error parsing video FPS '%s'. + + + + + Error parsing maximum time '%s'. + + + + + Error parsing maximum file size '%s'. + + + + + Invalid argument to '%s'. + + + + + Invalid parameters. + + + + + Enter password: + + + + + Failed to read new password from file. + + + + + + + + + + Incorrect arguments to '%s'. + + + + + Invalid vm-process-priority '%s'. + + + + + Invalid autostart delay number '%s'. + + + + + DHCPServer + + + Either --network or --interface, not both + + + + + Either --interface or --network, not both + + + + + Failed to locate host-only interface '%s' + + + + + Failed to find DHCP server for network '%s' + + + + + Failed to find DHCP server for host-only interface '%s' (network '%ls') + + + + + + You need to specify either --network or --interface to identify the DHCP server + + + + + Incomplete option sequence preseeding '--global' + + + + + Incomplete option sequence preseeding '--group' + + + + + Group name cannot be empty + + + + + Incomplete option sequence preseeding '--mac-address' + + + + + Incomplete option sequence preseeding '--vm' + + + + + --nic option requires a --vm preceeding selecting the VM it should apply to + + + + + Incomplete option sequence preseeding '--nic=%u + + + + + invalid NIC number: %u + + + + + Malformed hex string given to --set-opt-hex %u: %s + + + + + + --del-opt does not apply to the 'add' subcommand + + + + + --unforce-opt does not apply to the 'add' subcommand + + + + + --unsuppress-opt does not apply to the 'add' subcommand + + + + + --zap-options does not apply to the 'add' subcommand + + + + + --remove-config does not apply to the 'add' subcommand + + + + + --remove-config cannot be applied to the global config + + + + + --fixed-address can only be applied to a VM NIC or an MAC address + + + + + A group must be selected to perform condition alterations. + + + + + Condition value cannot be empty + + + + + Could not find any condition of type %d with value '%s' to delete + + + + + --zap-conditions can only be with a group selected + + + + + Incomplete option sequence preseeding '--id=%u + + + + + --value without --id=dhcp-opt-no + + + + + --remove does not apply to the 'add' subcommand + + + + + --remove without --id=dhcp-opt-no + + + + + Missing required option: --ip + + + + + Missing required option: --netmask + + + + + Missing required option: --lowerip + + + + + Missing required option: --upperip + + + + + Could not find interface '%s' + + + + + Could not get network name for the interface '%s' + + + + + DHCP server already exists + + + + + Failed to create the DHCP server + + + + + DHCP server does not exist + + + + + Failed to set configuration (%ls, %ls, %ls, %ls) + + + + + Failed to remove server + + + + + Failed to start the server + + + + + Failed to restart the server + + + + + Failed to stop the server + + + + + You need to specify a MAC address too look for + + + + + IP Address: %ls +MAC Address: %RTmac +State: %ls +Issued: %s (%RU64) +Expire: %s (%RU64) +TTL: %RU64 sec, currently %RU64 sec left + + + + + + DebugVM + + + The getregisters sub-command takes at least one register name + + + + + Must specify info item to display + + + + + The --compression option has already been given + + + + + The --filename option has already been given + + + + + The --filename option is required + + + + + RTPathAbs failed on '%s': %Rrc + + + + + Detected: %ls + + + + + + Name: %ls + + + + + + Version: %ls + + + + + + setregisters expects input on the form 'register=value' got '%s' + + + + + Out of memory + + + + + + The setregisters sub-command takes at least one register name + + + + + Successfully set %ls + + + + + + Successfully set %u registers + + + + + + + + export %s='%ls' + + + + + + set %s=%ls + + + + + + Debug logger settings: + + + + + + Release logger settings: + + + + + + The show sub-command has no idea what '%s' might be + + + + + ====================== CPU #%u ====================== + + + + + + Multiple --pattern options are not permitted + + + + + The --reset and --descriptions options does not mix + + + + + The --filename is missing + + + + + Machine '%s' is not currently running. + + + + + + Disk + + + Error code %Rrc at %s(%u) in function %s + + + + + + Cannot convert filename "%s" to absolute path + + + + + Out of memory copying '%s' + + + + + + + + + + + + + + + + + + + Only one command can be specified: '%s' + + + + + Invalid key value pair: No '='. + + + + + Cannot open replacement value file '%s': %Rrc + + + + + Error reading replacement MBR file '%s': %Rrc + + + + + Out of memory reading '%s': %Rrc + + + + + Replacement value file '%s' is to big: %Rhcb, max 16MiB + + + + + Cannot get the size of the value file '%s': %Rrc + + + + + + + + Invalid medium variant '%s' + + + + + + + + + + + Invalid parameter '%s' + + + + + + + + + Invalid option -%c + + + + + + + + + Invalid option case %i + + + + + + + + + unknown option: %s + + + + + + + + + + + error: %Rrs + + + + + + Parameters --filename is required + + + + + Parameters --size is required + + + + + Creating a differencing medium is only supported for hard disks + + + + + Invalid parent hard disk reference, avoiding crash + + + + + The %s is not found in the property list of the requested medium format. + + + + + Base64 encoding of the property %s failed. (%Rhrc) + + + + + Failed to create medium + + + + + Medium created. UUID: %s + + + + + + Invalid medium type '%s' + + + + + Invalid autoreset parameter '%s' + + + + + Invalid --property argument '%s' + + + + + Error: Failed to allocate memory for medium property '%s' + + + + + + + + Medium name or UUID required + + + + + No operation specified + + + + + Invalid medium reference, avoiding crash + + + + + Error: Attempt to resize the medium from %RU64.%RU64 MB to %RU64.%RU64 MB. Use --resizebyte if this is intended! + + + + + + Compact medium operation is not implemented! + + + + + Compact medium operation for this format is not implemented yet! + + + + + Failed to compact medium + + + + + Failed to compact medium! + + + + + Failed to resize medium + + + + + Resize medium operation is not implemented! + + + + + Resize medium operation for this format is not implemented yet! + + + + + Failed to resize medium! + + + + + Failed to move medium + + + + + Move medium with UUID %s finished + + + + + + Set new location of medium with UUID %s finished + + + + + + Medium description has been changed. + + + + + + unhandled option: -%c + + + + + unhandled option: %i + + + + + unknown option: %s + + + + + Mandatory UUID or input file parameter missing + + + + + Mandatory output file parameter missing + + + + + Specified options which cannot be used with --existing + + + + + Failed to clone medium + + + + + Clone medium created in format '%ls'. UUID: %s + + + + + + Invalid UUID '%s' + + + + + Incorrect number of parameters + + + + + Converting from raw image file="%s" to file="%s"... + + + + + + Cannot open file "%s": %Rrc + + + + + Cannot get image size for file "%s": %Rrc + + + + + Creating %s image with size %RU64 bytes (%RU64MB)... + + + + + + + + fixed + adjective + + + + + dynamic + adjective + + + + + Converted image from %s + + + + + Cannot create the virtual disk container: %Rrc + + + + + Cannot create the disk image "%s": %Rrc + + + + + Out of memory allocating buffers for image "%s": %Rrc + + + + + Failed to write to disk image "%s": %Rrc + + + + + Parent UUID: %s + + + + + + + + unknown + + + + + not created + + + + + created + + + + + locked read + + + + + locked write + + + + + inaccessible + + + + + creating + + + + + deleting + + + + + State: %s + + + + + + Access Error: %ls + + + + + + Description: %ls + + + + + + normal (differencing) + + + + + normal (base) + + + + + immutable + + + + + writethrough + + + + + shareable + + + + + readonly + + + + + multiattach + + + + + Type: %s + + + + + + Auto-Reset: %s + + + + + + on + + + + + off + + + + + Location: %ls + + + + + + Storage format: %ls + + + + + + split2G + + + + + streamOptimized + + + + + ESX + + + + + default + + + + + dynamic + + + + + fixed + + + + + differencing + + + + + Format variant: %s %s + + + + + + Capacity: %lld MBytes + + + + + + Size on disk: %lld MBytes + + + + + + Encryption: enabled + + + + + + Cipher: %ls + + + + + + Password ID: %ls + + + + + + Encryption: disabled + + + + + + Property: + + + + + In use by VMs: + + + + + Child UUIDs: + + + + + base + + + + + Failed to delete medium + + + + + Failed to delete medium. Error code %Rrc + + + + + unexpected parameter %s + + + + + + Missing action + + + + + Invalid action given: %s + + + + + Invalid number of arguments given for action: %s + + + + + Disk name or UUID required + + + + + No password specified + + + + + A new password must always have a valid identifier set at the same time + + + + + Enter new password: + + + + + Failed to read new password from file + + + + + Enter old password: + + + + + Failed to read old password from file + + + + + + Invalid hard disk reference, avoiding crash + + + + + Encrypt hard disk operation is not implemented! + + + + + Encrypt hard disk operation for this cipher is not implemented yet! + + + + + Failed to encrypt hard disk + + + + + Failed to encrypt hard disk! + + + + + Invalid number of arguments: %d + + + + + Enter password: + + + + + Failed to read password from file + + + + + The given password is correct + + + + + + No medium specified! + + + + + Enter encryption password: + + + + + + Error opening '%s' for writing: %Rrc + + + + + Specified offset (%#RX64) is beyond the end of the medium (%#RX64) + + + + + Read(%zu bytes at %#RX64) + + + + + + + ********** <ditto x %RU64> + + + + + + + Error writing to '%s': %Rrc + + + + + Expected read() at offset %RU64 (%#RX64) to return %#zx bytes, only got %#zx! + + + + + + + + + Error closing '%s': %Rrc + + + + + GuestCtrl + + + Unable to install console control handler, rc=%Rrc + + + + + + Unable to uninstall console control handler, rc=%Rrc + + + + + + + starting + + + + + + + started + + + + + paused + + + + + + terminating + + + + + successfully terminated + + + + + terminated by signal + + + + + abnormally aborted + + + + + + + timed out + + + + + + timed out, hanging + + + + + + + killed + + + + + + + + error + + + + + + + + + + unknown + + + + + + terminated + + + + + status changed + + + + + stdin ready + + + + + data on stdout + + + + + data on stderr + + + + + waiting flag not supported + + + + + opening + + + + + open + + + + + closing + + + + + closed + + + + + fifo + + + + + char-device + + + + + directory + + + + + block-device + + + + + file + + + + + symlink + + + + + socket + + + + + white-out + + + + + Error details: + + + + + Object has indicated no error (%Rhrc)!? + + + + + + Could not lookup progress information + + + + + + + Out of memory + + + + + The --username|-u option is ignored by '%s' + + + + + Password is given more than once. + + + + + The --password option is ignored by '%s' + + + + + The --password-file|-p option is ignored by '%s' + + + + + The --domain option is ignored by '%s' + + + + + Failed to get a IConsole pointer for the machine. Is it still running? + + + + + + Machine "%s" is not running (currently %s)! + + + + + + [%RU32] VBoxManage Guest Control [%s] - %s + + + + + No enough memory for session name + + + + + Creating guest session as user '%s'... + + + + + + Out of memory setting up IGuest::CreateSession call + + + + + Waiting for guest session to start... + + + + + + Out of memory setting up IGuestSession::WaitForArray call + + + + + Successfully started guest session (ID %RU32) + + + + + + Error starting guest session (current status is: %s) + + + + + + <unknown> + + + + + No user name specified! + + + + + Closing guest session ... + + + + + + Guest session detached + + + + + + Unable to write output, rc=%Rrc + + + + + + Unsupported %s line ending conversion + + + + + Error getting %s handle: %Rrc + + + + + Invalid argument variable[=value]: '%s' + + + + + Warning: Deprecated option "--no-profile" specified + + + + + + No executable specified! + + + + + Starting guest process ... + + + + + + Starting guest process (within %ums) + + + + + + Process '%s' (PID %RU32) started + + + + + + [%RU32 - Session %RU32] + + + + + + waitResult: %d + + + + + + Process terminated + + + + + + Process execution aborted! + + + + + + Process successfully started! + + + + + + Exit code=%u (Status=%u [%s]) + + + + + + Process timed out (guest side) and %s + + + + + + failed to terminate so far + + + + + was terminated + + + + + Process now is in status [%s] (unexpected) + + + + + + Process monitor loop quit with vrc=%Rrc + + + + + + Process monitor loop timed out + + + + + + No sources specified! + + + + + + No destination specified! + + + + + + RTPathAbs failed on '%s': %Rrc + + + + + Copying from host to guest ... + + + + + + Copying from guest to host ... + + + + + + + RTPathQueryInfo failed on '%s': %Rrc + + + + + Destination must be a directory! + + + + + + File '%s' -> '%s' + + + + + + + Directory '%s' -> '%s' + + + + + + + Not a file or directory: %s + + + + + + FsObjQueryInfo failed on '%s': %Rhrc + + + + + File copy failed + + + + + Creating %RU32 directories... + + + + + + + + mkdir was interrupted by Ctrl-C (%u left) + + + + + + Creating directory "%s" ... + + + + + + + + Out of memory + + + + + + No directory to create specified! + + + + + Removing %RU32 directory tree(s)... + + + + + + + + Removing %RU32 directorie(s)... + + + + + + + + rmdir was interrupted by Ctrl-C (%u left) + + + + + + Removing directory "%s" ... + + + + + + Recursively removing directory "%s" ... + + + + + + Directory deletion failed + + + + + Out of memory during recursive rmdir + + + + + + No directory to remove specified! + + + + + Removing %RU32 file(s)... + + + + + + + + rm was interrupted by Ctrl-C (%u left) + + + + + + Removing file "%s" ... + + + + + + No file to remove specified! + + + + + Failed to initialize, rc=%Rrc + + + + + + No source(s) to move specified! + + + + + Destination does not exist + + + + + + Destination must be a directory when specifying multiple sources + + + + + + Unable to determine destination type: %Rhrc + + + + + + Renaming %RU32 %s ... + + + + + + sources + + + + + + + source + + + + + Cannot stat "%s": No such file or directory + + + + + + Renaming %s "%s" to "%s" ... + + + + + + directory + object + + + + + file + object + + + + + Warning: Not all sources were renamed + + + + + + More than one template specified! + + + + + + No template specified! + + + + + Creating temporary files is currently not supported! + + + + + Creating temporary directory from template '%s' in directory '%s' ... + + + + + + Creating temporary directory from template '%s' in default temporary directory ... + + + + + + Creating temporary file from template '%s' in directory '%s' ... + + + + + + Creating temporary file from template '%s' in default temporary directory ... + + + + + + Directory name: %ls + + + + + + Command "%s" not implemented yet! + + + + + Nothing to stat! + + + + + Checking for element "%s" ... + + + + + + Failed to stat '%s': No such file + + + + + + File: '%s' + + + + + + Size: %-17RU64 Alloc: %-19RU64 Type: %s + + + + + + Device: %#-17RX32 INode: %-18RU64 Links: %u + + + + + + Mode: %-16s Attrib: %-17s Dev ID: %#RX32 + + + + + + Mode: %-16s Attrib: %s + + + + + + Owner: %4d/%-12ls Group: %4d/%ls + + + + + + Birth: %s + + + + + + Change: %s + + + + + + Modify: %s + + + + + + Access: %s + + + + + + Current run level is %RU32 + + + + + + Waiting for run level %RU32 ... + + + + + + + Waiting failed with %Rrc + + + + + + Run level %RU32 reached + + + + + + Run level %RU32 not reached within time + + + + + + RTPathAbsCxx failed on '%s': %Rrc + + + + + Updating Guest Additions ... + + + + + + No Guest Additions source found or specified, aborting + + + + + + Source "%s" does not exist! + + + + + + OS type: + + + + + Additions run level: + + + + + Additions version: + + + + + Using source: %s + + + + + + Waiting for current Guest Additions inside VM getting ready for updating ... + + + + + + Guest Additions %lsr%RU64 currently installed, waiting for Guest Additions installer to start ... + + + + + + Guest Additions update failed + + + + + Guest Additions update successful. + + + + + + Rebooting guest ... + + + + + + Current installed Guest Additions don't support automatic rebooting. Please reboot manually. + + + + + + Waiting for new Guest Additions inside VM getting ready ... + + + + + + Verifying Guest Additions update ... + + + + + + Old Guest Additions: %ls%RU64 + + + + + + New Guest Additions: %ls%RU64 + + + + + + +Error updating Guest Additions, please check guest installer log + + + + + + +WARNING: Guest Additions were downgraded + + + + + + The guest needs to be restarted in order to make use of the updated Guest Additions. + + + + + + Invalid run level specified. Valid values are: system, userland, desktop + + + + + Missing run level to wait for + + + + + Unknown list: '%s' + + + + + Missing list name + + + + + Active guest sessions: + + + + + + + Session #%-3zu ID=%-3RU32 User=%-16ls Status=[%s] Name=%ls + + + + + + Process #%-03zu PID=%-6RU32 Status=[%s] Command=%ls + + + + + + File #%-03zu ID=%-6RU32 Status=[%s] Name=%ls + + + + + + +Total guest sessions: %zu + + + + + + Total guest processes: %zu + + + + + + Total guest files: %zu + + + + + + No active guest sessions found + + + + + + Invalid PID value: 0 + + + + + Error parsing PID value: %Rrc + + + + + At least one PID must be specified to kill! + + + + + + No session ID specified! + + + + + + Either session ID or name (pattern) must be specified + + + + + Terminating process (PID %RU32) (session ID %RU32) ... + + + + + + No matching process(es) for session ID %RU32 found + + + + + + No matching session(s) found + + + + + + %RU32 process(es) terminated + + + + + + + + Closing guest session ID=#%RU32 "%s" ... + + + + + + Guest session successfully closed + + + + + + No guest session(s) found + + + + + + Waiting for events ... + + + + + + Unknown sub-command: '%s' + + + + + Missing sub-command + + + + + Missing VM name and sub-command + + + + + GuestCtrlLsnr + + + File ID=%RU32 "%s" changed status to [%s] + + + + + + Process PID=%RU32 "%s" changed status to [%s] + + + + + + File "%s" %s + + + + + + + + registered + + + + + + + unregistered + + + + + + + Registering ... + + + + + + Unregistering file ... + + + + + + Process "%s" %s + + + + + + Unregistering process ... + + + + + + Session ID=%RU32 "%s" changed status to [%s] + + + + + + Session ID=%RU32 "%s" %s + + + + + + Unregistering ... + + + + + + Reached run level %RU32 + + + + + + GuestProp + + + + + + + + + Incorrect parameters + + + + + No value set! + + + + + + Value: %ls + + + + + + Timestamp: %lld + + + + + + Flags: %ls + + + + + + No properties found. + + + + + + Name: %ls, value: %ls, timestamp: %lld, flags: %ls + + + + + + Property %ls was deleted + + + + + + Name: %ls, value: %ls, flags: %ls + + + + + + Time out or interruption while waiting for a notification. + + + + + Help + + + Usage - %s%s: + + + + + + Usage: + + + + + + No subcommand specified + + + + + Unknown subcommand: %s + + + + + Too many parameters + + + + + Invalid parameter '%s' + + + + + Invalid option -%c + + + + + Invalid option case %i + + + + + Unknown option: %s + + + + + Invalid argument format: %s + + + + + Missing the %u%s value for option %s + + + + + st + + + + + nd + + + + + rd + + + + + th + + + + + HostOnly + + + Failed to create the host-only adapter + + + + + Interface '%ls' was successfully created + + + + + + + Only one interface name can be specified + + + + + No interface name was specified + + + + + Failed to remove the host-only adapter + + + + + The --ip option is specified more than once + + + + + The --netmask option is specified more than once + + + + + The --ipv6 option is specified more than once + + + + + The --netmasklengthv6 option is specified more than once + + + + + You can not use --dhcp with static ip configuration parameters: --ip, --netmask, --ipv6 and --netmasklengthv6. + + + + + You can not use ipv4 configuration (--ip and --netmask) with ipv6 (--ipv6 and --netmasklengthv6) simultaneously. + + + + + Could not find interface '%s' + + + + + IPv6 setting is not supported for this adapter + + + + + Neither -dhcp nor -ip nor -ipv6 was specfified + + + + + + No sub-command specified + + + + + + Unknown sub-command '%s' + + + + + The --name parameter must be specified + + + + + The --netmask parameter must be specified + + + + + The --lower-ip parameter must be specified + + + + + The --upper-ip parameter must be specified + + + + + + Either --name or --id parameter must be specified + + + + + Info + + + + + + + + + enabled + + + + + + + + + + + + + disabled + + + + + %sName: %ls (UUID: %s)%s + + + + + + %sDescription: %ls + + + + + + %sDescription: +%ls + + + + + + powered off + + + + + saved + + + + + teleported + + + + + aborted + + + + + aborted-saved + + + + + running + + + + + + paused + + + + + guru meditation + + + + + teleporting + + + + + live snapshotting + + + + + starting + + + + + stopping + + + + + saving + + + + + restoring + + + + + teleporting paused vm + + + + + teleporting (incoming) + + + + + deleting snapshot live + + + + + deleting snapshot live paused + + + + + online snapshotting + + + + + restoring snapshot + + + + + deleting snapshot + + + + + setting up + + + + + offline snapshotting + + + + + + + + + + + + + unknown + + + + + + + + not active + + + + + pre-initializing + + + + + initializing + + + + + active/running + + + + + terminating + + + + + terminated + + + + + failed + + + + + + + Null + + + + + Disk + + + + + + Network + + + + + + <none> + + + + + + #%zu: Name: '%ls', Type: %s, Limit: none (disabled) + + + + + + #%zu: Name: '%ls', Type: %s, Limit: %RI64 %s (%RI64 %s) + + + + + + #%zu: Name: '%ls', Type: %s, Limit: %RI64 %s + + + + + + Name: '%ls', Host path: '%ls' (%s), %s%s + + + + + writable + + + + + readonly + + + + + , auto-mount + + + + + , mount-point: '%ls' + + + + + + VendorId: + + + + + ProductId: + + + + + Revision: + + + + + Manufacturer: + + + + + Product: + + + + + SerialNumber: + + + + + Address: + + + + + + + + <none> + + + + + Port %u, Unit %u: UUID: %ls%s%s%s%s%s%s + Location: "%ls" + + + + + + , passthrough enabled + + + + + + , temp eject + + + + + + , ejected + + + + + , hot-pluggable + + + + + , non-rotational (SSD) + + + + + , discards unused blocks + + + + + Port %u, Unit %u: Empty%s%s + + + + + + Port %u, Unit %u: GetMedium failed: %Rhrc + + + + + + + + + None + + + + + Automatic + + + + + + + + + + + + + + Unknown + + + + + Default + + + + + Legacy + + + + + Minimal + + + + + "<inaccessible>" {%s} + + + + + + Name: <inaccessible!> + + + + + + Config file: %ls + + + + + + Access error details: + + + + + + Name: + + + + + Groups: + + + + + + Guest OS: + + + + + Config file: + + + + + Snapshot folder: + + + + + Log folder: + + + + + Hardware UUID: + + + + + Memory size: + + + + + Page Fusion: + + + + + VRAM size: + + + + + CPU exec cap: + + + + + HPET: + + + + + CPUProfile: + + + + + invalid + + + + + Chipset: + + + + + Firmware: + + + + + Number of CPUs: + + + + + Long Mode: + + + + + Triple Fault Reset: + + + + + Nested VT-x/AMD-V: + + + + + CPUID Portability Level: + + + + + CPUID overrides: + + + + + Leaf no. EAX EBX ECX EDX + + + + + + None + + + + + + menu only + + + + + message and menu + + + + + Boot menu mode: + + + + + Floppy + + + + + HardDisk + + + + + Shared Folder + + + + + Not Assigned + + + + + Boot Device %u: + + + + + BIOS APIC mode: + + + + + Time offset: + + + + + ms + + + + + BIOS NVRAM File: + + + + + RTC: + + + + + local time + + + + + Hardware Virtualization: + + + + + Nested Paging: + + + + + Large Pages: + + + + + VT-x Unrestricted Exec.: + + + + + AMD-V Virt. Vmsave/Vmload: + + + + + Paravirt. Provider: + + + + + Effective Paravirt. Prov.: + + + + + Paravirt. Debug: + + + + + %-28s %s (since %s) + + + + + + State: + + + + + Graphics Controller: + + + + + Monitor count: + + + + + 3D Acceleration: + + + + + 2D Video Acceleration: + + + + + Teleporter Enabled: + + + + + Teleporter Port: + + + + + Teleporter Address: + + + + + Teleporter Password: + + + + + Tracing Enabled: + + + + + Allow Tracing to Access VM: + + + + + Tracing Configuration: + + + + + Autostart Enabled: + + + + + Autostart Delay: + + + + + Default Frontend: + + + + + flat + + + + + low + + + + + normal + + + + + high + + + + + default + + + + + VM process priority: + + + + + + Storage Controllers: + + + + + #%u: '%ls', Type: %s, Instance: %u, Ports: %u (max %u), %s + + + + + + Bootable + + + + + Not bootable + + + + + NIC %u: + + + + + + + + + %-28s disabled + + + + + + + + none + + + + + %sNIC %d Rule(%d): name = %s, protocol = %s, host ip = %s, host port = %s, guest ip = %s, guest port = %s + + + + + + NIC %d Settings: MTU: %d, Socket (send: %d, receive: %d), TCP Window (send:%d, receive: %d) + + + + + + Bridged Interface '%ls' + + + + + Internal Network '%s' + + + + + Host-only Interface '%ls' + + + + + Generic '%ls' + + + + + NAT Network '%s' + + + + + Host Only Network '%s' + + + + + Cloud Network '%s' + + + + + deny + + + + + allow-vms + + + + + allow-all + + + + + %-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 + + + + + + + + + on + + + + + + + + off + + + + + PS/2 Mouse + + + + + USB Mouse + + + + + USB Tablet + + + + + USB Tablet and PS/2 Mouse + + + + + USB Multi-Touch + + + + + Pointing Device: + + + + + PS/2 Keyboard + + + + + USB Keyboard + + + + + USB and PS/2 Keyboard + + + + + Keyboard Device: + + + + + UART %u: + + + + + + %-28s I/O base: %#06x, IRQ: %d + + + + + , disconnected + + + + + , attached to raw file '%ls' + + + + + + , attached to tcp (%s) '%ls' + + + + + + server + + + + + + client + + + + + , attached to pipe (%s) '%ls' + + + + + , attached to device '%ls' + + + + + LPT %u: + + + + + , attached to device '%ls' + + + + + + Audio: + + + + + (Driver: %s, Controller: %s, Codec: %s) + + + + + Audio playback: + + + + + Audio capture: + + + + + + HostToGuest + + + + + + GuestToHost + + + + + + Bidirectional + + + + + Clipboard Mode: + + + + + Clipboard file transfers: + + + + + Drag and drop Mode: + + + + + Session name: + + + + + unknown status + + + + + blank + + + + + Video mode: + + + + + null + + + + + external + + + + + guest + + + + + %-28s enabled (Address %ls, Ports %ls, MultiConn: %s, ReuseSingleConn: %s, Authentication type: %s) + + + + + + VRDE port: + + + + + %-28s enabled (Quality %ls) + + + + + + + Video redirection: + + + + + %-28s: %-10lS = <not set> + + + + + + + VRDE property + + + + + USB Device Filters: + + + + + + Index: + + + + + Active: + + + + + yes + + + + + no + + + + + Name: + + + + + VendorId: + + + + + ProductId: + + + + + Revision: + + + + + Manufacturer: + + + + + Product: + + + + + Remote: + + + + + Serial Number: + + + + + Masked Interfaces: + + + + + USB Device Filters: + + + + + Available remote USB devices: + + + + + Currently attached USB devices: + + + + + +Attached physical PCI devices: + + + + + + + Host device %ls at %s attached as %s + + + + + + Bandwidth groups: + + + + + Shared folders: + + + + + global mapping + + + + + machine mapping + + + + + transient mapping + + + + + VRDE Connection: + + + + + + + active + + + + + Clients so far: + + + + + Start time: + + + + + Last started: + + + + + Last ended: + + + + + Sent: + + + + + + + + Bytes + + + + + Average speed: + + + + + + B/s + + + + + Sent total: + + + + + Received: + + + + + Speed: + + + + + Received total: + + + + + User name: + + + + + Domain: + + + + + Client name: + + + + + Client IP: + + + + + Client version: + + + + + Encryption: + + + + + Capturing: + + + + + Capture audio: + + + + + Capture screens: + + + + + Capture file: + + + + + Capture dimensions: + + + + + Capture rate: + + + + + + kbps + + + + + Capture FPS: + + + + + Capture options: + + + + + Description: +%ls + + + + + + VMMDev Testing + + + + + + misconfigured + + + + + * Snapshots: + + + + + + * Guest: + + + + + + Configured memory balloon: + + + + + MB + + + + + OS type: + + + + + Additions run level: + + + + + Additions version: + + + + + + Guest Facilities: + + + + + Facility "%ls": %s (last update: %s) + + + + + + Invalid parameter '%s' + + + + + VM name or UUID required + + + + + Option --log is exclusive + + + + + Internal + + + Usage: VBoxManage internalcommands <command> [command arguments] + +Commands: + +%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%sWARNING: This is a development tool and shall only be used to analyse + problems. It is completely unsupported and will change in + incompatible ways without warning. + + + + + + loadmap <vmname|uuid> <symfile> <address> [module] [subtrahend] [segment] + This will instruct DBGF to load the given map file + during initialization. (See also loadmap in the debugger.) + + + + + + + loadsyms <vmname|uuid> <symfile> [delta] [module] [module address] + This will instruct DBGF to load the given symbol file + during initialization. + + + + + + + sethduuid <filepath> [<uuid>] + Assigns a new UUID to the given image file. This way, multiple copies + of a container can be registered. + + + + + + + sethdparentuuid <filepath> <uuid> + Assigns a new parent UUID to the given image file. + + + + + + + dumphdinfo <filepath> + Prints information about the image at the given location. + + + + + + + listpartitions -rawdisk <diskname> + Lists all partitions on <diskname>. + + + + + + + createrawvmdk -filename <filename> -rawdisk <diskname> + [-partitions <list of partition numbers> [-mbr <filename>] ] + [-relative] + Creates a new VMDK image which gives access to an entire host disk (if + the parameter -partitions is not specified) or some partitions of a + host disk. If access to individual partitions is granted, then the + parameter -mbr can be used to specify an alternative MBR to be used + (the partitioning information in the MBR file is ignored). + The diskname is on Linux e.g. /dev/sda, and on Windows e.g. + \\.\PhysicalDrive0). + On Linux or FreeBSD host the parameter -relative causes a VMDK file to + be created which refers to individual partitions instead to the entire + disk. + The necessary partition numbers can be queried with + VBoxManage internalcommands listpartitions + + + + + + + renamevmdk -from <filename> -to <filename> + Renames an existing VMDK image, including the base file and all its extents. + + + + + + + converttoraw [-format <fileformat>] <filename> <outputfile>|stdout + Convert image to raw, writing to file or stdout. + + + + + + + converttoraw [-format <fileformat>] <filename> <outputfile> + Convert image to raw, writing to file. + + + + + + + converthd [-srcformat VDI|VMDK|VHD|RAW] + [-dstformat VDI|VMDK|VHD|RAW] + <inputfile> <outputfile> + converts hard disk images between formats + + + + + + + repairhd [-dry-run] + [-format VDI|VMDK|VHD|...] + <filename> + Tries to repair corrupted disk images + + + + + + + modinstall + Installs the necessary driver for the host OS + + + + + + + moduninstall + Deinstalls the driver + + + + + + + debuglog <vmname|uuid> [--enable|--disable] [--flags todo] + [--groups todo] [--destinations todo] + Controls debug logging. + + + + + + + passwordhash <password> + Generates a password hash. + + + + + + + gueststats <vmname|uuid> [--interval <seconds>] + Obtains and prints internal guest statistics. + Sets the update interval if specified. + + + + + + + Cannot find unique key for '%s'! + + + + + Failed to delete key '%s' from '%s', string conversion error %Rrc! + + + + + Failed to set '%s/%s/%s' to '%s'! hrc=%#x + + + + + + Missing the filename argument! + + + + + + Failed to read delta '%s', rc=%Rrc + + + + + + + Failed to read module address '%s', rc=%Rrc + + + + + + Failed to read module size '%s', rc=%Rrc + + + + + + Missing the module address argument! + + + + + + Failed to read subtrahend '%s', rc=%Rrc + + + + + + Failed to read segment number '%s', rc=%Rrc + + + + + + Error code %Rrc at %s(%u) in function %s + + + + + + + Not enough parameters + + + + + + Invalid UUID parameter + + + + + Invalid invocation + + + + + + Format autodetect failed: %Rrc + + + + + + + + + Cannot create the virtual disk container: %Rrc + + + + + + Cannot open the image: %Rrc + + + + + Cannot set a new UUID: %Rrc + + + + + UUID changed to: %s + + + + + + The GPT header seems corrupt because it contains too many entries + + + + + Allocating memory for the GPT partitions entries failed + + + + + Reading the partition table failed + + + + + More than one extended partition + + + + + Inconsistency for logical partition start + + + + + Logical partition without magic + + + + + Logical partition with type 0 encountered + + + + + Invalid partition start offset + + + + + Logical partition chain broken + + + + + Two partitions start at the same place + + + + + Partition starts at sector 0 + + + + + Overlapping GPT partitions + + + + + Overlapping MBR partitions + + + + + + + + + + + + + + + Missing argument to '%s' + + + + + + + + + + + Invalid parameter '%s' + + + + + +Syntax error: %N + + + + + + Invalid option -%c + + + + + Invalid option case %i + + + + + Unknown option: %s + + + + + Invalid argument format: %s + + + + + + Mandatory parameter -rawdisk missing + + + + + Cannot open the raw disk: %Rrc + + + + + Number Type StartCHS EndCHS Size (MiB) Start (Sect) + + + + + + Mandatory parameter -filename missing + + + + + The parameter -mbr is only valid when the parameter -partitions is also present + + + + + Cannot open the raw disk '%s': %Rrc + + + + + File '%s' is no fixed/removable medium device + + + + + The -relative parameter is invalid for raw disk %s + + + + + Cannot get the geometry of the raw disk '%s': %Rrc + + + + + + + The -relative parameter is invalid for raw images + + + + + + + Cannot get the size of the raw disk '%s': %Rrc + + + + + + + Failed to get size of file '%s': %Rrc + + + + + File '%s' is no block device + + + + + + + + Failed to get file informtation for raw disk '%s': %Rrc + + + + + Cannot get the block size for file '%s': %Rrc + + + + + + Cannot get the block count for file '%s': %Rrc + + + + + File '%s' is neither block device nor regular file + + + + + File '%s' is no block or char device + + + + + File '%s' is neither character device nor regular file + + + + + Detected size of raw disk '%s' is %RU64, an invalid value + + + + + Incorrect value in partitions parameter + + + + + Incorrect separator in partitions parameter + + + + + Cannot read the partition information from '%s' + + + + + 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. + + + + + + Out of memory allocating the partition list for '%s' + + + + + Out of memory allocating the partition descriptor for '%s' + + + + + Cannot read partition data from raw device '%s': %Rrc + + + + + Cannot open replacement MBR file '%s' specified with -mbr: %Rrc + + + + + Cannot read replacement MBR file '%s': %Rrc + + + + + Cannot create reference to individual partition %u, rc=%Rrc + + + + + Cannot create reference to individual partition %u (numbered %u), rc=%Rrc + + + + + MBR/EPT overlaps with data area + + + + + GPT overlaps with data area + + + + + Cannot create the raw disk VMDK: %Rrc + + + + + RAW host disk access VMDK file %s created successfully. + + + + + + The raw disk vmdk file was not created + + + + + Mandatory parameter -from missing + + + + + Mandatory parameter -to missing + + + + + Cannot rename the image: %Rrc + + + + + Cannot create the source image: %Rrc + + + + + Mandatory filename parameter missing + + + + + Mandatory outputfile parameter missing + + + + + Cannot create destination file "%s": %Rrc + + + + + + + No file format specified and autodetect failed - please specify format: %Rrc + + + + + Only converting harddisk images is supported + + + + + + Cannot open the source image: %Rrc + + + + + Converting image "%s" with size %RU64 bytes (%RU64MB) to raw... + + + + + + + + Cannot copy image data: %Rrc + + + + + Out of memory allocating read buffer + + + + + + Mandatory input image parameter missing + + + + + Mandatory output image parameter missing + + + + + Cannot create the source virtual disk container: %Rrc + + + + + Cannot create the destination virtual disk container: %Rrc + + + + + Converting image "%s" with size %RU64 bytes (%RU64MB)... + + + + + + + + Cannot copy the image: %Rrc + + + + + + Missing VM name/UUID + + + + + One or more of the requested features are not implemented! Feel free to do this. + + + + + password to hash required + + + + + Password hash: %s + + + + + + Invalid update interval specified + + + + + argc=%d interval=%u + + + + + + Command missing + + + + + Invalid command '%s' + + + + + List + + + + + + + Unknown + + + + + + unknown + + + + + Up + + + + + Down + + + + + HardDisk + + + + + Floppy + + + + + Network + + + + + SharedFolder + + + + + Graphics3D + + + + + + Name: %ls + + + + + + + + + Name: %ls + + + + + + + + Enabled + + + + + + + Disabled + + + + + IPAddress: %ls + + + + + + + NetworkMask: %ls + + + + + + IPV6Address: %ls + + + + + + IPV6NetworkMaskPrefixLength: %d + + + + + + HardwareAddress: %ls + + + + + + MediumType: %s + + + + + + Wireless: %s + + + + + + + Yes + + + + + + No + + + + + Status: %s + + + + + + + + VBoxNetworkName: %ls + + + + + + + + State: %s + + + + + + LowerIP: %ls + + + + + + UpperIP: %ls + + + + + + CloudProvider: %ls + + + + + + CloudProfile: %ls + + + + + + CloudNetworkId: %ls + + + + + + HW virtualization + + + + + long mode + + + + + nested paging + + + + + unrestricted guest + + + + + nested HW virtualization + + + + + virt. vmsave/vmload + + + + + Host Information: + + + + + + + Host time: %s + + + + + + Processor online count: %lu + + + + + + Processor count: %lu + + + + + + Processor online core count: %lu + + + + + + Processor core count: %lu + + + + + + Processor supports %s: %s + + + + + + + + + + + yes + + + + + + + + + + no + + + + + Processor#%u speed: %lu MHz + + + + + + Processor#%u speed: unknown + + + + + + Processor#%u description: %ls + + + + + + Memory size: %lu MByte + + + + + + + + Memory available: %lu MByte + + + + + + + + Operating system: %ls + + + + + + Operating system version: %ls + + + + + + Supported hard disk backends: + + + + + + + Backend %u: id='%ls' description='%ls' capabilities=%#06x extensions=' + + + + + properties=( + + + + + + name='%ls' desc='%ls' type= + + + + + int + + + + + byte + + + + + string + + + + + flags=%#04x + + + + + default='%ls' + + + + + Host USB Devices: + + + + + + + + <none> + + + + + + + UUID: %s +VendorId: %#06x (%04X) +ProductId: %#06x (%04X) +Revision: %u.%u (%02u%02u) +Port: %u + + + + + + Low + + + + + Full + + + + + High + + + + + Super + + + + + SuperPlus + + + + + USB version/speed: %u/%s + + + + + + Manufacturer: %ls + + + + + + Product: %ls + + + + + + SerialNumber: %ls + + + + + + Address: %ls + + + + + + Port path: %ls + + + + + + Not supported + + + + + Unavailable + + + + + Busy + + + + + Available + + + + + Held + + + + + Captured + + + + + Current State: %s + + + + + + + Global USB Device Filters: + + + + + + + Index: %zu + + + + + + Active: %s + + + + + + <invalid> + + + + + Ignore + + + + + Hold + + + + + Action: %s + + + + + + Name: %ls + + + + + + VendorId: %ls + + + + + + ProductId: %ls + + + + + + Revision: %ls + + + + + + Manufacturer: %ls + + + + + + Product: %ls + + + + + + Serial Number: %ls + + + + + + + API version: %ls + + + + + + Minimum guest RAM size: %u Megabytes + + + + + + + + Maximum guest RAM size: %u Megabytes + + + + + + + + Minimum video RAM size: %u Megabytes + + + + + + + + Maximum video RAM size: %u Megabytes + + + + + + + + Maximum guest monitor count: %u + + + + + + Minimum guest CPU count: %u + + + + + + Maximum guest CPU count: %u + + + + + + Virtual disk limit (info): %lld Bytes + + + + + + + + Maximum Serial Port count: %u + + + + + + Maximum Parallel Port count: %u + + + + + + Maximum Boot Position: %u + + + + + + Maximum PIIX3 Network Adapter count: %u + + + + + + Maximum ICH9 Network Adapter count: %u + + + + + + Maximum PIIX3 IDE Controllers: %u + + + + + + Maximum ICH9 IDE Controllers: %u + + + + + + Maximum IDE Port count: %u + + + + + + Maximum Devices per IDE Port: %u + + + + + + Maximum PIIX3 SATA Controllers: %u + + + + + + Maximum ICH9 SATA Controllers: %u + + + + + + Maximum SATA Port count: %u + + + + + + Maximum Devices per SATA Port: %u + + + + + + Maximum PIIX3 SCSI Controllers: %u + + + + + + Maximum ICH9 SCSI Controllers: %u + + + + + + Maximum SCSI Port count: %u + + + + + + Maximum Devices per SCSI Port: %u + + + + + + Maximum SAS PIIX3 Controllers: %u + + + + + + Maximum SAS ICH9 Controllers: %u + + + + + + Maximum SAS Port count: %u + + + + + + Maximum Devices per SAS Port: %u + + + + + + Maximum NVMe PIIX3 Controllers: %u + + + + + + Maximum NVMe ICH9 Controllers: %u + + + + + + Maximum NVMe Port count: %u + + + + + + Maximum Devices per NVMe Port: %u + + + + + + Maximum virtio-scsi PIIX3 Controllers: %u + + + + + + Maximum virtio-scsi ICH9 Controllers: %u + + + + + + Maximum virtio-scsi Port count: %u + + + + + + Maximum Devices per virtio-scsi Port: %u + + + + + + Maximum PIIX3 Floppy Controllers:%u + + + + + + Maximum ICH9 Floppy Controllers: %u + + + + + + Maximum Floppy Port count: %u + + + + + + Maximum Devices per Floppy Port: %u + + + + + + Free disk space warning at: %u Bytes + + + + + + + + Free disk space warning at: %u %% + + + + + + Free disk space error at: %u Bytes + + + + + + + + Free disk space error at: %u %% + + + + + + Default machine folder: %ls + + + + + + Raw-mode Supported: %s + + + + + + Exclusive HW virtualization use: %s + + + + + + + + on + + + + + + + off + + + + + Default hard disk format: %ls + + + + + + VRDE auth library: %ls + + + + + + Webservice auth. library: %ls + + + + + + Remote desktop ExtPack: %ls + + + + + + Log history count: %u + + + + + + Default frontend: %ls + + + + + + Null + + + + + Default audio driver: %s + + + + + + Autostart database path: %ls + + + + + + Default Guest Additions ISO: %ls + + + + + + Logging Level: %ls + + + + + + System + + + + + NoProxy + + + + + Manual + + + + + Proxy Mode: %s + + + + + + Proxy URL: %ls + + + + + + Stable: new minor and maintenance releases + + + + + All releases: new minor, maintenance, and major releases + + + + + With Betas: new minor, maintenance, major, and beta releases + + + + + Unset + + + + + Last check date: %ls + + + + + + User language: %ls + + + + + + Enabled: %s + + + + + + Check count: %u + + + + + + Check frequency: never + + + + + + Check frequency: every day + + + + + + Check frequency: every %u days + + + + + + + + Channel: %s + + + + + + Repository: %ls + + + + + + minLeaseTime: default + + + + + + minLeaseTime: %u sec + + + + + + defaultLeaseTime: default + + + + + + defaultLeaseTime: %u sec + + + + + + maxLeaseTime: default + + + + + + maxLeaseTime: %u sec + + + + + + Forced options: %Rhrc + + + + + + Forced options: None + + + + + + Forced options: + + + + + Suppressed opt.s: %Rhrc + + + + + + Suppressed opts.: None + + + + + + Suppressed opts.: + + + + + DHCP options: %Rhrc + + + + + + DHCP options: Return count mismatch: %zu, %zu, %zu + + + + + + DHCP options: None + + + + + + %3d/legacy: %ls + + + + + + NetworkName: %ls + + + + + + LowerIPAddress: %ls + + + + + + UpperIPAddress: %ls + + + + + + NetworkMask: %ls + + + + + + Enabled: %s + + + + + + Global Configuration: + + + + + + Groups: %Rrc + + + + + + Groups: None + + + + + + Group: %ls + + + + + + Conditions: %Rhrc + + + + + + Conditions: None + + + + + + Conditions: %s %s %ls + + + + + + include + + + + + exclude + + + + + Individual Configs: %Rrc + + + + + + Individual Configs: None + + + + + + Individual Config: MAC %ls + + + + + + Individual Config: VM NIC: %ls slot %u, MAC %ls + + + + + + Individual Config: VM NIC: %ls slot %u, MAC %Rhrc + + + + + + Fixed Address: %ls + + + + + + Fixed Address: dynamic + + + + + + Extension Packs: %u + + + + + + Pack no.%2zu: %ls +Version: %ls +Revision: %u +Edition: %ls +Description: %ls +VRDE Module: %ls +Usable: %RTbool +Why unusable: %ls + + + + + + Video Input Devices: %u + + + + + + Supported %d screen shot formats: + + + + + + + + Supported %d cloud providers: + + + + + + + + Short Name: %ls + + + + + + Name: %ls + + + + + + Provider GUID: %ls + + + + + + Property: + + + + + CPU Profile #%02zu: + + + + + + Architecture: %s + + + + + + Name: %ls + + + + + + Full Name: %ls + + + + + + %sDrive: %ls + + + + + + %sDrive: %Rhrc + + + + + + Model: %Rhrc + + + + + + Model: "%ls" + + + + + + Model: unknown/inaccessible + + + + + + Further disk and partitioning information is not available for drive "%ls". (E_ACCESSDENIED) + + + + + + Size: %llu bytes (%Rhcb) + + + + + + + + Size: %Rhcb + + + + + + Size: %Rhrc + + + + + + Sector Size: %u bytes + + + + + + + + Sector Size: %Rhrc + + + + + + Scheme: %s + + + + + + Scheme: %Rhrc + + + + + + Partitions: %Rhrc + + + + + + Partitions: None (or not able to grok them). + + + + + + Partitions: First Last +## Type Byte Size Byte Offset Cyl/Head/Sec Cyl/Head/Sec Active + + + + + + Partitions: First Last +## Type Size Start Cyl/Head/Sec Cyl/Head/Sec Active + + + + + + Partitions: +## %-*s Uuid Byte Size Byte Offset Active Name + + + + + + + Type + + + + + Partitions: +## %-*s Uuid Size Start Active Name + + + + + + Description: %ls + + + + + + Family ID: %ls + + + + + + Family Desc: %ls + + + + + + 64 bit: %RTbool + + + + + + + Name: %ls + + + + + + + Host CPUIDs: + +Leaf no. EAX EBX ECX EDX + + + + + + base + + + + + Unknown subcommand "%s". + + + + + Missing subcommand for "list" command. + + + + + + Metrics + + + Invalid machine name: '%s' + + + + + host + + + + + unknown + + + + + The following metrics were modified: + +Object Metric +---------- -------------------- + + + + + + No metrics match the specified filter! + + + + + Object Metric Unit Minimum Maximum Period Count Description +--------------- ---------------------------------------- ---- ---------- ---------- ---------- ---------- ----------- + + + + + + + + + Missing argument to '%s' + + + + + + Invalid value for 'period' parameter: '%s' + + + + + + Invalid value for 'samples' parameter: '%s' + + + + + Object Metric Values +--------------- ---------------------------------------- -------------------------------------------- + + + + + + The background process holding collected metrics will shutdown +in few seconds, discarding all collected data and parameters. + + + + + Time stamp Object Metric Value + + + + + + Subcommand missing + + + + + Invalid subcommand '%s' + + + + + Misc + + + + + + + Incorrect number of parameters + + + + + + Cannot convert filename "%s" to absolute path: %Rrc + + + + + + + + Invalid parameter '%s' + + + + + + Invalid option -%c + + + + + + Invalid option case %i + + + + + + unknown option: %s + + + + + + + error: %Rrs + + + + + + + VM name required + + + + + Machine delete failed + + + + + Parameter --name is required + + + + + Virtual machine '%ls' is created%s. +UUID: %s +Settings file: '%ls' + + + + + + and registered + + + + + + RTPathAbs(%s,,) failed with rc=%Rrc + + + + + Move VM failed + + + + + Machine has been successfully moved into %s + + + + + + the same location + + + + + Invalid clone mode '%s' + + + + + + Invalid clone options '%s' + + + + + + %s Clone + + + + + Clone VM failed + + + + + Machine has been successfully cloned as "%ls" + + + + + + Parameter to option --putenv must not contain any newline character + + + + + at least one VM name or uuid required + + + + + Waiting for VM "%s" to power on... + + + + + + VM "%s" has been successfully started. + + + + + + + Key: %ls, Value: %ls + + + + + + + Value: %ls + + + + + + + No value set! + + + + + + + Not enough parameters + + + + + + Too many parameters + + + + + Invalid hwvirtexclusive argument '%s' + + + + + Warning: 'vrdpauthlibrary' is deprecated. Use 'vrdeauthlibrary'. + + + + + + Error parsing Log history count '%s' + + + + + Unknown proxy mode: '%s' + + + + + + Machine name is given more than once: first '%s', then '%s' + + + + + + No machine was specified + + + + + + No shared folder name (--name) was given + + + + + Invalid shared folder name '%s': contains space + + + + + Invalid shared folder name '%s': contains tabs + + + + + Invalid shared folder name '%s': contains newline + + + + + No host path (--hostpath) was given + + + + + RTAbsPath failed on '%s': %Rrc + + + + + Machine '%s' is not currently running. + + + + + Machine '%s' is not currently running. + + + + + + + Too many extension pack names given to "extpack uninstall" + + + + + No extension pack name was given to "extpack install" + + + + + License accepted. + + + + + + Do you agree to these license terms and conditions (y/n)? + + + + + Installation of "%ls" aborted. + + + + + + License accepted. For batch installation add +--accept-license=%s +to the VBoxManage command line. + + + + + + + Failed to install "%s" + + + + + Successfully installed "%ls". + + + + + + No extension pack name was given to "extpack uninstall" + + + + + Failed to uninstall "%s" + + + + + Successfully uninstalled "%s". + + + + + + Successfully performed extension pack cleanup + + + + + + + + + + + + RTPathAbs failed on '%s': %Rrc + + + + + No ISO specified + + + + + Detected '%s' to be: + + + + + + OS TypeId = %ls + OS Version = %ls + OS Flavor = %ls + OS Languages = %ls + OS Hints = %ls + + + + + + VM name/UUID given more than once! + + + + + Missing VM name/UUID + + + + + Machine '%ls' is currently running + + + + + %s unattended installation of %s in machine '%ls' (%ls). + + + + + + Preparing + + + + + Starting + + + + + Using values: + + + + + + + + %32s = failed: %Rhrc + + + + + + %32 = failed: %Rhrc + + + + + + VM '%ls' (%ls) is ready to be started (e.g. VBoxManage startvm). + + + + + + Waiting for VM '%ls' to power on... + + + + + + VM '%ls' (%ls) has been successfully started. + + + + + + + + + Parameter --provider is required + + + + + + + + Parameter --profile is required + + + + + Provider %ls: profile '%ls' was updated. + + + + + + Provider GUID: %ls + + + + + + Property: + + + + + Provider %ls: profile '%ls' was added. + + + + + + Provider %ls: profile '%ls' was deleted. + + + + + + ModifyVM + + + Warning: '--vrdp%s' is deprecated. Use '--vrde%s'. + + + + + + Invalid %s number %u + + + + + + Not enough parameters + + + + + Cannot open file "%s": %Rrc + + + + + Cannot get size of file "%s": %Rrc + + + + + File "%s" is bigger than 256KByte + + + + + Cannot read contents of file "%s": %Rrc + + + + + Invalid --firmware argument '%s' + + + + + Invalid --paravirtprovider argument '%s' + + + + + + + + + + Missing or invalid argument to '%s' + + + + + Invalid --graphicscontroller argument '%s' + + + + + Invalid --biosbootmenu argument '%s' + + + + + Invalid --biosapic argument '%s' + + + + + Invalid boot device '%s' + + + + + Invalid --idecontroller argument '%s' + + + + + Invalid --usb argument '%s' + + + + + Invalid --scsitype argument '%s' + + + + + + Invalid host DVD drive name "%s" + + + + + Invalid host floppy drive name "%s" + + + + + Invalid --nicproperty%d argument '%s' + + + + + Error: Failed to allocate memory for --nicproperty%d '%s' + + + + + + Invalid NIC type '%s' specified for NIC %u + + + + + Invalid boot priority '%u' specfied for NIC %u + + + + + Unknown promiscuous mode policy '%s' + + + + + Invalid type '%s' specfied for NIC %u + + + + + Invalid proto '%s' specfied for NIC %u + + + + + Invalid type '%s' specfied for pointing device + + + + + Invalid type '%s' specfied for keyboard + + + + + Invalid argument to '%s' + + + + + Error parsing UART I/O base '%s' + + + + + Error parsing LPT I/O base '%s' + + + + + Invalid --audiocontroller argument '%s' + + + + + Invalid --audiocodec argument '%s' + + + + + Invalid --audio argument '%s' + + + + + Invalid --clipboard-mode argument '%s' + + + + + Invalid --clipboard-file-transfers argument '%s' + + + + + Invalid --draganddrop argument '%s' + + + + + Invalid --vrdeproperty argument '%s' + + + + + Error: Failed to allocate memory for VRDE property '%s' + + + + + + Invalid --vrdeauthtype argument '%s' + + + + + Invalid --usbrename parameters, nothing renamed + + + + + *** I/O APIC must be enabled for ICH9, enabling. *** + + + + + + Invalid --chipset argument '%s' (valid: piix3,ich9) + + + + + Invalid --iommu argument '%s' (valid: none,amd,automatic) + + + + + Warning: On Intel hosts, 'automatic' will not enable an IOMMU since the Intel IOMMU device is not supported yet. + + + + + + Invalid --iommu argument '%s' + + + + + Invalid --tpm-type argument '%s' + + + + + Invalid list of screens specified + + + + + + Cannot convert filename "%s" to absolute path + + + + + + + Error parsing video resolution '%s' (expected <width>x<height>) + + + + + Invalid --autostop-type argument '%s' (valid: disabled, savestate, poweroff, acpishutdown) + + + + + Invalid --pciattach argument '%s' (valid: 'HB:HD.HF@GB:GD.GF' or just 'HB:HD.HF') + + + + + Invalid --pcidetach argument '%s' (valid: 'HB:HD.HF') + + + + + Invalid --vm-process-priority '%s' + + + + + --testing-cfg-dword index %u is out of range: 0 thru 9 + + + + + Nat + + + Name: %ls + + + + + + Network: %ls + + + + + + Gateway: %ls + + + + + + + + + Yes + + + + + + + + No + + + + + IPv6 Prefix: %ls + + + + + + IPv6 Default: %s + + + + + + Enabled: %s + + + + + + DHCP Server: %s + + + + + + Port-forwarding (ipv4) + + + + + + Port-forwarding (ipv6) + + + + + + loopback mappings (ipv4) + + + + + + NAT Networks: + + + + + + + %zu %s found + + + + + + network + + + + + networks + + + + + + + + Not enough parameters + + + + + You can only specify --netname only once. + + + + + You can only specify --network only once. + + + + + + You can specify either --enable or --disable once. + + + + + You can specify --dhcp only once. + + + + + You can specify --ipv6 only once. + + + + + You can specify --ipv6-prefix only once. + + + + + You can specify --ipv6-default only once. + + + + + loopback couldn't be deleted on modified + + + + + + + Not enough parŠ°meters + + + + + + Invalid port-forward rule %s + + + + + + Port-forward could be deleted on modify + + + + + + Port-forward rule name is too long + + + + + + You need to specify the --netname option + + + + + You need to specify the --network option + + + + + Unknown operation (:%d) + + + + + NATNetwork server already exists + + + + + Failed to create the NAT network service + + + + + NATNetwork server does not exist + + + + + + + + + + + Failed to set configuration + + + + + Failed to delete pf + + + + + Failed to add pf + + + + + + invalid loopback string + + + + + Failed to remove nat network + + + + + Failed to start network + + + + + Failed to stop network + + + + + Invalid parameter '%s' + + + + + Nvram + + + No platform key file path was given to "enrollpk" + + + + + No owner UUID was given to "enrollpk" + + + + + Cannot read contents of file "%s": %Rrc + + + + + File "%s" is bigger than 32KByte + + + + + Cannot get size of file "%s": %Rrc + + + + + Cannot open file "%s": %Rrc + + + + + No variable name was given to "queryvar" + + + + + Error writing to '%s': %Rrc + + + + + + Error opening '%s': %Rrc + + + + + No variable name was given to "deletevar" + + + + + No owner UUID was given to "deletevar" + + + + + No variable name was given to "changevar" + + + + + No variable data filename was given to "changevar" + + + + + Error reading from '%s': %Rrc + + + + + Snapshot + + + + + This machine does not have any snapshots + + + + + + [%RI32] Images and snapshots for medium "%ls" + + + + + + Not enough parameters + + + + + + Missing snapshot name + + + + + Invalid unique name description '%s' + + + + + Failed to generate a unique snapshot name + + + + + Snapshot taken. UUID: %ls + + + + + + Failed to take snapshot + + + + + Too many arguments + + + + + + Expecting snapshot name only + + + + + %s snapshot '%ls' (%ls) + + + + + + Deleting + + + + + Restoring + + + + + Snapshot operation failed + + + + + Invalid parameter '%s' + + + + + Storage + + + + Invalid --type argument '%s' + + + + + Invalid medium type '%s' + + + + + Storage controller name not specified + + + + + Drive passthrough state cannot be changed while the VM is running + + + + + + Bandwidth group cannot be changed while the VM is running + + + + + + Could not find a controller named '%s' + + + + + + Port not specified + + + + + Device not specified + + + + + No DVD/Floppy Drive attached to the controller '%s'at the port: %u, device: %u + + + + + The attachment is not supported by the storage controller '%s' + + + + + Cannot find the Guest Additions ISO image + + + + + + Argument --type must be specified + + + + + + The given attachment is not supported by the storage controller '%s' + + + + + + Invalid host DVD drive name "%s" + + + + + Invalid host floppy drive name "%s" + + + + + Parameters --server and --target are required for iSCSI media + + + + + iSCSI disk created. UUID: %s + + + + + + Missing --medium argument + + + + + Invalid UUID or filename "%s" + + + + + Failed to set the medium/parent medium UUID + + + + + Failed to set the medium type + + + + + Invalid --passthrough argument '%s' + + + + + + + + + Couldn't find the controller attachment for the controller '%s' + + + + + + Invalid --tempeject argument '%s' + + + + + Invalid --nonrotational argument '%s' + + + + + Invalid --discard argument '%s' + + + + + Invalid --hotpluggable argument '%s' + + + + + Too few parameters + + + + + Storage controller name not specified + + + + + + Invalid --add argument '%s' + + + + + + + + + Couldn't find the controller with the name: '%s' + + + + + + Invalid --hostiocache argument '%s' + + + + + Invalid --bootable argument '%s' + + + + + UpdateCheck + + + Enabled: %s + + + + + + yes + + + + + no + + + + + Count: %u + + + + + + Frequency: never + + + + + + Frequency: every day + + + + + + Frequency: every %u days + + + + + + Stable - new minor and maintenance releases + + + + + All releases - new minor, maintenance, and major releases + + + + + With Betas - new minor, maintenance, major, and beta releases + + + + + Unset + + + + + Channel: %s + + + + + + Unknown channel specified: '%s' + + + + + Checking for a new %ls version... + + + + + + Failed to create update progress object: %Rhrc + + + + + + Checking for update failed. + + + + + A new version of %ls has been released! Version %ls is available at virtualbox.org. +You can download this version here: %ls + + + + + + You are already running the most recent version of %ls. + + + + + + Something went wrong while checking for updates! +Please check network connection and try again later. + + + + + + Last Check Date: %ls + + + + + + The update frequency cannot be zero + + + + + No change requested + + + + + Usb + + + + + + + Not enough parameters + + + + + Invalid parameter '%s' + + + + + Invalid index '%s' + + + + + + + + + + + + + + + + + Missing argument to '%s' + + + + + Invalid --active argument '%s' + + + + + Failed to convert the --maskedinterfaces value '%s' to a number, vrc=%Rrc + + + + + Invalid USB filter action '%s' + + + + + Unknown option '%s' + + + + + + Mandatory options not supplied + + + + + Invalid number of parameters + + + + + Parameter "%s" is invalid + + + + + Utils + + + type bridged + + + + + type host-only + + + + + unknown type %RU32 + + + + + Interface "%s" is of %s + + + + + Interface "%s" doesn't seem to exist + + + + + VBoxManage + + + + Progress object failure: %Rhrc + + + + + + Failed to get progress description: %Rhrc + + + + + + (%u/%u) %ls %02u%% => %02u%% (%d s remaining) + + + + + + CANCELED + + + + + + Progress state: %Rhrc + + + + + + Password expected + + + + + No password file specified + + + + + Only one response file allowed + + + + + Error reading response file '%s': %Rrc + + + + + Invalid response file ('%s') encoding: %Rrc + + + + + Failed to parse response file '%s' (bourne shell style): %Rrc + + + + + out of memory + + + + + commands: + + + + + + Invalid command '%s' + + + + + Failed to initialize COM because the global settings directory '%s' is not accessible! + + + + + Failed to initialize COM! (hrc=%Rhrc) + + + + + Failed to create a session object! + + + + + Failed to create the VirtualBox object! + + + + + Most likely, the VirtualBox COM server is not running or failed to start. + + + + -- cgit v1.2.3