summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/src-server/UnattendedImpl.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
commitf8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch)
tree26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/Main/src-server/UnattendedImpl.cpp
parentInitial commit. (diff)
downloadvirtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.tar.xz
virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.zip
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Main/src-server/UnattendedImpl.cpp')
-rw-r--r--src/VBox/Main/src-server/UnattendedImpl.cpp2349
1 files changed, 2349 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/UnattendedImpl.cpp b/src/VBox/Main/src-server/UnattendedImpl.cpp
new file mode 100644
index 00000000..01db6b67
--- /dev/null
+++ b/src/VBox/Main/src-server/UnattendedImpl.cpp
@@ -0,0 +1,2349 @@
+/* $Id: UnattendedImpl.cpp $ */
+/** @file
+ * Unattended class implementation
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_MAIN_UNATTENDED
+#include "LoggingNew.h"
+#include "VirtualBoxBase.h"
+#include "UnattendedImpl.h"
+#include "UnattendedInstaller.h"
+#include "UnattendedScript.h"
+#include "VirtualBoxImpl.h"
+#include "SystemPropertiesImpl.h"
+#include "MachineImpl.h"
+#include "Global.h"
+
+#include <VBox/err.h>
+#include <iprt/ctype.h>
+#include <iprt/file.h>
+#include <iprt/fsvfs.h>
+#include <iprt/inifile.h>
+#include <iprt/locale.h>
+#include <iprt/path.h>
+
+using namespace std;
+
+/* XPCOM doesn't define S_FALSE. */
+#ifndef S_FALSE
+# define S_FALSE ((HRESULT)1)
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Controller slot for a DVD drive.
+ *
+ * The slot can be free and needing a drive to be attached along with the ISO
+ * image, or it may already be there and only need mounting the ISO. The
+ * ControllerSlot::fFree member indicates which it is.
+ */
+struct ControllerSlot
+{
+ StorageBus_T enmBus;
+ Utf8Str strControllerName;
+ ULONG uPort;
+ ULONG uDevice;
+ bool fFree;
+
+ ControllerSlot(StorageBus_T a_enmBus, const Utf8Str &a_rName, ULONG a_uPort, ULONG a_uDevice, bool a_fFree)
+ : enmBus(a_enmBus), strControllerName(a_rName), uPort(a_uPort), uDevice(a_uDevice), fFree(a_fFree)
+ {}
+
+ bool operator<(const ControllerSlot &rThat) const
+ {
+ if (enmBus == rThat.enmBus)
+ {
+ if (strControllerName == rThat.strControllerName)
+ {
+ if (uPort == rThat.uPort)
+ return uDevice < rThat.uDevice;
+ return uPort < rThat.uPort;
+ }
+ return strControllerName < rThat.strControllerName;
+ }
+
+ /*
+ * Bus comparsion in boot priority order.
+ */
+ /* IDE first. */
+ if (enmBus == StorageBus_IDE)
+ return true;
+ if (rThat.enmBus == StorageBus_IDE)
+ return false;
+ /* SATA next */
+ if (enmBus == StorageBus_SATA)
+ return true;
+ if (rThat.enmBus == StorageBus_SATA)
+ return false;
+ /* SCSI next */
+ if (enmBus == StorageBus_SCSI)
+ return true;
+ if (rThat.enmBus == StorageBus_SCSI)
+ return false;
+ /* numerical */
+ return (int)enmBus < (int)rThat.enmBus;
+ }
+
+ bool operator==(const ControllerSlot &rThat) const
+ {
+ return enmBus == rThat.enmBus
+ && strControllerName == rThat.strControllerName
+ && uPort == rThat.uPort
+ && uDevice == rThat.uDevice;
+ }
+};
+
+/**
+ * Installation disk.
+ *
+ * Used when reconfiguring the VM.
+ */
+typedef struct UnattendedInstallationDisk
+{
+ StorageBus_T enmBusType; /**< @todo nobody is using this... */
+ Utf8Str strControllerName;
+ DeviceType_T enmDeviceType;
+ AccessMode_T enmAccessType;
+ ULONG uPort;
+ ULONG uDevice;
+ bool fMountOnly;
+ Utf8Str strImagePath;
+
+ UnattendedInstallationDisk(StorageBus_T a_enmBusType, Utf8Str const &a_rBusName, DeviceType_T a_enmDeviceType,
+ AccessMode_T a_enmAccessType, ULONG a_uPort, ULONG a_uDevice, bool a_fMountOnly,
+ Utf8Str const &a_rImagePath)
+ : enmBusType(a_enmBusType), strControllerName(a_rBusName), enmDeviceType(a_enmDeviceType), enmAccessType(a_enmAccessType)
+ , uPort(a_uPort), uDevice(a_uDevice), fMountOnly(a_fMountOnly), strImagePath(a_rImagePath)
+ {
+ Assert(strControllerName.length() > 0);
+ }
+
+ UnattendedInstallationDisk(std::list<ControllerSlot>::const_iterator const &itDvdSlot, Utf8Str const &a_rImagePath)
+ : enmBusType(itDvdSlot->enmBus), strControllerName(itDvdSlot->strControllerName), enmDeviceType(DeviceType_DVD)
+ , enmAccessType(AccessMode_ReadOnly), uPort(itDvdSlot->uPort), uDevice(itDvdSlot->uDevice)
+ , fMountOnly(!itDvdSlot->fFree), strImagePath(a_rImagePath)
+ {
+ Assert(strControllerName.length() > 0);
+ }
+} UnattendedInstallationDisk;
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+*
+*
+* Implementation Unattended functions
+*
+*/
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+Unattended::Unattended()
+ : mhThreadReconfigureVM(NIL_RTNATIVETHREAD), mfRtcUseUtc(false), mfGuestOs64Bit(false)
+ , mpInstaller(NULL), mpTimeZoneInfo(NULL), mfIsDefaultAuxiliaryBasePath(true), mfDoneDetectIsoOS(false)
+{ }
+
+Unattended::~Unattended()
+{
+ if (mpInstaller)
+ {
+ delete mpInstaller;
+ mpInstaller = NULL;
+ }
+}
+
+HRESULT Unattended::FinalConstruct()
+{
+ return BaseFinalConstruct();
+}
+
+void Unattended::FinalRelease()
+{
+ uninit();
+
+ BaseFinalRelease();
+}
+
+void Unattended::uninit()
+{
+ /* Enclose the state transition Ready->InUninit->NotReady */
+ AutoUninitSpan autoUninitSpan(this);
+ if (autoUninitSpan.uninitDone())
+ return;
+
+ unconst(mParent) = NULL;
+ mMachine.setNull();
+}
+
+/**
+ * Initializes the unattended object.
+ *
+ * @param aParent Pointer to the parent object.
+ */
+HRESULT Unattended::initUnattended(VirtualBox *aParent)
+{
+ LogFlowThisFunc(("aParent=%p\n", aParent));
+ ComAssertRet(aParent, E_INVALIDARG);
+
+ /* Enclose the state transition NotReady->InInit->Ready */
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ unconst(mParent) = aParent;
+
+ /*
+ * Fill public attributes (IUnattended) with useful defaults.
+ */
+ try
+ {
+ mStrUser = "vboxuser";
+ mStrPassword = "changeme";
+ mfInstallGuestAdditions = false;
+ mfInstallTestExecService = false;
+ midxImage = 1;
+
+ HRESULT hrc = mParent->i_getSystemProperties()->i_getDefaultAdditionsISO(mStrAdditionsIsoPath);
+ ComAssertComRCRet(hrc, hrc);
+ }
+ catch (std::bad_alloc &)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ /*
+ * Confirm a successful initialization
+ */
+ autoInitSpan.setSucceeded();
+
+ return S_OK;
+}
+
+HRESULT Unattended::detectIsoOS()
+{
+ HRESULT hrc;
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+/** @todo once UDF is implemented properly and we've tested this code a lot
+ * more, replace E_NOTIMPL with E_FAIL. */
+
+
+ /*
+ * Open the ISO.
+ */
+ RTVFSFILE hVfsFileIso;
+ int vrc = RTVfsFileOpenNormal(mStrIsoPath.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFileIso);
+ if (RT_FAILURE(vrc))
+ return setErrorBoth(E_NOTIMPL, vrc, tr("Failed to open '%s' (%Rrc)"), mStrIsoPath.c_str(), vrc);
+
+ RTERRINFOSTATIC ErrInfo;
+ RTVFS hVfsIso;
+ vrc = RTFsIso9660VolOpen(hVfsFileIso, 0 /*fFlags*/, &hVfsIso, RTErrInfoInitStatic(&ErrInfo));
+ if (RT_SUCCESS(vrc))
+ {
+ /*
+ * Try do the detection. Repeat for different file system variations (nojoliet, noudf).
+ */
+ hrc = i_innerDetectIsoOS(hVfsIso);
+
+ RTVfsRelease(hVfsIso);
+ hrc = E_NOTIMPL;
+ }
+ else if (RTErrInfoIsSet(&ErrInfo.Core))
+ hrc = setErrorBoth(E_NOTIMPL, vrc, tr("Failed to open '%s' as ISO FS (%Rrc) - %s"),
+ mStrIsoPath.c_str(), vrc, ErrInfo.Core.pszMsg);
+ else
+ hrc = setErrorBoth(E_NOTIMPL, vrc, tr("Failed to open '%s' as ISO FS (%Rrc)"), mStrIsoPath.c_str(), vrc);
+ RTVfsFileRelease(hVfsFileIso);
+
+ /*
+ * Just fake up some windows installation media locale (for <UILanguage>).
+ * Note! The translation here isn't perfect. Feel free to send us a patch.
+ */
+ if (mDetectedOSLanguages.size() == 0)
+ {
+ char szTmp[16];
+ const char *pszFilename = RTPathFilename(mStrIsoPath.c_str());
+ if ( pszFilename
+ && RT_C_IS_ALPHA(pszFilename[0])
+ && RT_C_IS_ALPHA(pszFilename[1])
+ && (pszFilename[2] == '-' || pszFilename[2] == '_') )
+ {
+ szTmp[0] = (char)RT_C_TO_LOWER(pszFilename[0]);
+ szTmp[1] = (char)RT_C_TO_LOWER(pszFilename[1]);
+ szTmp[2] = '-';
+ if (szTmp[0] == 'e' && szTmp[1] == 'n')
+ strcpy(&szTmp[3], "US");
+ else if (szTmp[0] == 'a' && szTmp[1] == 'r')
+ strcpy(&szTmp[3], "SA");
+ else if (szTmp[0] == 'd' && szTmp[1] == 'a')
+ strcpy(&szTmp[3], "DK");
+ else if (szTmp[0] == 'e' && szTmp[1] == 't')
+ strcpy(&szTmp[3], "EE");
+ else if (szTmp[0] == 'e' && szTmp[1] == 'l')
+ strcpy(&szTmp[3], "GR");
+ else if (szTmp[0] == 'h' && szTmp[1] == 'e')
+ strcpy(&szTmp[3], "IL");
+ else if (szTmp[0] == 'j' && szTmp[1] == 'a')
+ strcpy(&szTmp[3], "JP");
+ else if (szTmp[0] == 's' && szTmp[1] == 'v')
+ strcpy(&szTmp[3], "SE");
+ else if (szTmp[0] == 'u' && szTmp[1] == 'k')
+ strcpy(&szTmp[3], "UA");
+ else if (szTmp[0] == 'c' && szTmp[1] == 's')
+ strcpy(szTmp, "cs-CZ");
+ else if (szTmp[0] == 'n' && szTmp[1] == 'o')
+ strcpy(szTmp, "nb-NO");
+ else if (szTmp[0] == 'p' && szTmp[1] == 'p')
+ strcpy(szTmp, "pt-PT");
+ else if (szTmp[0] == 'p' && szTmp[1] == 't')
+ strcpy(szTmp, "pt-BR");
+ else if (szTmp[0] == 'c' && szTmp[1] == 'n')
+ strcpy(szTmp, "zh-CN");
+ else if (szTmp[0] == 'h' && szTmp[1] == 'k')
+ strcpy(szTmp, "zh-HK");
+ else if (szTmp[0] == 't' && szTmp[1] == 'w')
+ strcpy(szTmp, "zh-TW");
+ else if (szTmp[0] == 's' && szTmp[1] == 'r')
+ strcpy(szTmp, "sr-Latn-CS"); /* hmm */
+ else
+ {
+ szTmp[3] = (char)RT_C_TO_UPPER(pszFilename[0]);
+ szTmp[4] = (char)RT_C_TO_UPPER(pszFilename[1]);
+ szTmp[5] = '\0';
+ }
+ }
+ else
+ strcpy(szTmp, "en-US");
+ try
+ {
+ mDetectedOSLanguages.append(szTmp);
+ }
+ catch (std::bad_alloc &)
+ {
+ return E_OUTOFMEMORY;
+ }
+ }
+
+ /** @todo implement actual detection logic. */
+ return hrc;
+}
+
+HRESULT Unattended::i_innerDetectIsoOS(RTVFS hVfsIso)
+{
+ DETECTBUFFER uBuf;
+ VBOXOSTYPE enmOsType = VBOXOSTYPE_Unknown;
+ HRESULT hrc = i_innerDetectIsoOSWindows(hVfsIso, &uBuf, &enmOsType);
+ if (hrc == S_FALSE && enmOsType == VBOXOSTYPE_Unknown)
+ hrc = i_innerDetectIsoOSLinux(hVfsIso, &uBuf, &enmOsType);
+ if (enmOsType != VBOXOSTYPE_Unknown)
+ {
+ try { mStrDetectedOSTypeId = Global::OSTypeId(enmOsType); }
+ catch (std::bad_alloc &) { hrc = E_OUTOFMEMORY; }
+ }
+ return hrc;
+}
+
+/**
+ * Detect Windows ISOs.
+ *
+ * @returns COM status code.
+ * @retval S_OK if detected
+ * @retval S_FALSE if not fully detected.
+ *
+ * @param hVfsIso The ISO file system.
+ * @param pBuf Read buffer.
+ * @param penmOsType Where to return the OS type. This is initialized to
+ * VBOXOSTYPE_Unknown.
+ */
+HRESULT Unattended::i_innerDetectIsoOSWindows(RTVFS hVfsIso, DETECTBUFFER *pBuf, VBOXOSTYPE *penmOsType)
+{
+ /** @todo The 'sources/' path can differ. */
+
+ // globalinstallorder.xml - vista beta2
+ // sources/idwbinfo.txt - ditto.
+ // sources/lang.ini - ditto.
+
+ /*
+ * Try look for the 'sources/idwbinfo.txt' file containing windows build info.
+ * This file appeared with Vista beta 2 from what we can tell. Before windows 10
+ * it contains easily decodable branch names, after that things goes weird.
+ */
+ RTVFSFILE hVfsFile;
+ int vrc = RTVfsFileOpen(hVfsIso, "sources/idwbinfo.txt", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
+ if (RT_SUCCESS(vrc))
+ {
+ *penmOsType = VBOXOSTYPE_WinNT_x64;
+
+ RTINIFILE hIniFile;
+ vrc = RTIniFileCreateFromVfsFile(&hIniFile, hVfsFile, RTINIFILE_F_READONLY);
+ RTVfsFileRelease(hVfsFile);
+ if (RT_SUCCESS(vrc))
+ {
+ vrc = RTIniFileQueryValue(hIniFile, "BUILDINFO", "BuildArch", pBuf->sz, sizeof(*pBuf), NULL);
+ if (RT_SUCCESS(vrc))
+ {
+ LogRelFlow(("Unattended: sources/idwbinfo.txt: BuildArch=%s\n", pBuf->sz));
+ if ( RTStrNICmp(pBuf->sz, RT_STR_TUPLE("amd64")) == 0
+ || RTStrNICmp(pBuf->sz, RT_STR_TUPLE("x64")) == 0 /* just in case */ )
+ *penmOsType = VBOXOSTYPE_WinNT_x64;
+ else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("x86")) == 0)
+ *penmOsType = VBOXOSTYPE_WinNT;
+ else
+ {
+ LogRel(("Unattended: sources/idwbinfo.txt: Unknown: BuildArch=%s\n", pBuf->sz));
+ *penmOsType = VBOXOSTYPE_WinNT_x64;
+ }
+ }
+
+ vrc = RTIniFileQueryValue(hIniFile, "BUILDINFO", "BuildBranch", pBuf->sz, sizeof(*pBuf), NULL);
+ if (RT_SUCCESS(vrc))
+ {
+ LogRelFlow(("Unattended: sources/idwbinfo.txt: BuildBranch=%s\n", pBuf->sz));
+ if ( RTStrNICmp(pBuf->sz, RT_STR_TUPLE("vista")) == 0
+ || RTStrNICmp(pBuf->sz, RT_STR_TUPLE("winmain_beta")) == 0)
+ *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_WinVista);
+ else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("win7")) == 0)
+ *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win7);
+ else if ( RTStrNICmp(pBuf->sz, RT_STR_TUPLE("winblue")) == 0
+ || RTStrNICmp(pBuf->sz, RT_STR_TUPLE("winmain_blue")) == 0
+ || RTStrNICmp(pBuf->sz, RT_STR_TUPLE("win81")) == 0 /* not seen, but just in case its out there */ )
+ *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win81);
+ else if ( RTStrNICmp(pBuf->sz, RT_STR_TUPLE("win8")) == 0
+ || RTStrNICmp(pBuf->sz, RT_STR_TUPLE("winmain_win8")) == 0 )
+ *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win8);
+ else
+ LogRel(("Unattended: sources/idwbinfo.txt: Unknown: BuildBranch=%s\n", pBuf->sz));
+ }
+ RTIniFileRelease(hIniFile);
+ }
+ }
+
+ /*
+ * Look for sources/lang.ini and try parse it to get the languages out of it.
+ */
+ /** @todo We could also check sources/??-* and boot/??-* if lang.ini is not
+ * found or unhelpful. */
+ vrc = RTVfsFileOpen(hVfsIso, "sources/lang.ini", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
+ if (RT_SUCCESS(vrc))
+ {
+ RTINIFILE hIniFile;
+ vrc = RTIniFileCreateFromVfsFile(&hIniFile, hVfsFile, RTINIFILE_F_READONLY);
+ RTVfsFileRelease(hVfsFile);
+ if (RT_SUCCESS(vrc))
+ {
+ mDetectedOSLanguages.clear();
+
+ uint32_t idxPair;
+ for (idxPair = 0; idxPair < 256; idxPair++)
+ {
+ size_t cbHalf = sizeof(*pBuf) / 2;
+ char *pszKey = pBuf->sz;
+ char *pszValue = &pBuf->sz[cbHalf];
+ vrc = RTIniFileQueryPair(hIniFile, "Available UI Languages", idxPair,
+ pszKey, cbHalf, NULL, pszValue, cbHalf, NULL);
+ if (RT_SUCCESS(vrc))
+ {
+ try
+ {
+ mDetectedOSLanguages.append(pszKey);
+ }
+ catch (std::bad_alloc &)
+ {
+ RTIniFileRelease(hIniFile);
+ return E_OUTOFMEMORY;
+ }
+ }
+ else if (vrc == VERR_NOT_FOUND)
+ break;
+ else
+ Assert(vrc == VERR_BUFFER_OVERFLOW);
+ }
+ if (idxPair == 0)
+ LogRel(("Unattended: Warning! Empty 'Available UI Languages' section in sources/lang.ini\n"));
+ RTIniFileRelease(hIniFile);
+ }
+ }
+
+ /** @todo look at the install.wim file too, extracting the XML (easy) and
+ * figure out the available image numbers and such. The format is
+ * documented. */
+
+ return S_FALSE;
+}
+
+/**
+ * Detects linux architecture.
+ *
+ * @returns true if detected, false if not.
+ * @param pszArch The architecture string.
+ * @param penmOsType Where to return the arch and type on success.
+ * @param enmBaseOsType The base (x86) OS type to return.
+ */
+static bool detectLinuxArch(const char *pszArch, VBOXOSTYPE *penmOsType, VBOXOSTYPE enmBaseOsType)
+{
+ if ( RTStrNICmp(pszArch, RT_STR_TUPLE("amd64")) == 0
+ || RTStrNICmp(pszArch, RT_STR_TUPLE("x86_64")) == 0
+ || RTStrNICmp(pszArch, RT_STR_TUPLE("x86-64")) == 0 /* just in case */
+ || RTStrNICmp(pszArch, RT_STR_TUPLE("x64")) == 0 /* ditto */ )
+ {
+ *penmOsType = (VBOXOSTYPE)(enmBaseOsType | VBOXOSTYPE_x64);
+ return true;
+ }
+
+ if ( RTStrNICmp(pszArch, RT_STR_TUPLE("x86")) == 0
+ || RTStrNICmp(pszArch, RT_STR_TUPLE("i386")) == 0
+ || RTStrNICmp(pszArch, RT_STR_TUPLE("i486")) == 0
+ || RTStrNICmp(pszArch, RT_STR_TUPLE("i586")) == 0
+ || RTStrNICmp(pszArch, RT_STR_TUPLE("i686")) == 0
+ || RTStrNICmp(pszArch, RT_STR_TUPLE("i786")) == 0
+ || RTStrNICmp(pszArch, RT_STR_TUPLE("i886")) == 0
+ || RTStrNICmp(pszArch, RT_STR_TUPLE("i986")) == 0)
+ {
+ *penmOsType = enmBaseOsType;
+ return true;
+ }
+
+ /** @todo check for 'noarch' since source CDs have been seen to use that. */
+ return false;
+}
+
+static bool detectLinuxDistroName(const char *pszOsAndVersion, VBOXOSTYPE *penmOsType, const char **ppszNext)
+{
+ bool fRet = true;
+
+ if ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("Red")) == 0
+ && !RT_C_IS_ALNUM(pszOsAndVersion[3]))
+
+ {
+ pszOsAndVersion = RTStrStripL(pszOsAndVersion + 3);
+ if ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("Hat")) == 0
+ && !RT_C_IS_ALNUM(pszOsAndVersion[3]))
+ {
+ *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_RedHat);
+ pszOsAndVersion = RTStrStripL(pszOsAndVersion + 3);
+ }
+ else
+ fRet = false;
+ }
+ else if ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("Oracle")) == 0
+ && !RT_C_IS_ALNUM(pszOsAndVersion[6]))
+ {
+ *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Oracle);
+ pszOsAndVersion = RTStrStripL(pszOsAndVersion + 6);
+ }
+ else if ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("CentOS")) == 0
+ && !RT_C_IS_ALNUM(pszOsAndVersion[6]))
+ {
+ *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_RedHat);
+ pszOsAndVersion = RTStrStripL(pszOsAndVersion + 6);
+ }
+ else if ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("Fedora")) == 0
+ && !RT_C_IS_ALNUM(pszOsAndVersion[6]))
+ {
+ *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_FedoraCore);
+ pszOsAndVersion = RTStrStripL(pszOsAndVersion + 6);
+ }
+ else
+ fRet = false;
+
+ /*
+ * Skip forward till we get a number.
+ */
+ if (ppszNext)
+ {
+ *ppszNext = pszOsAndVersion;
+ char ch;
+ for (const char *pszVersion = pszOsAndVersion; (ch = *pszVersion) != '\0'; pszVersion++)
+ if (RT_C_IS_DIGIT(ch))
+ {
+ *ppszNext = pszVersion;
+ break;
+ }
+ }
+ return fRet;
+}
+
+
+/**
+ * Detect Linux distro ISOs.
+ *
+ * @returns COM status code.
+ * @retval S_OK if detected
+ * @retval S_FALSE if not fully detected.
+ *
+ * @param hVfsIso The ISO file system.
+ * @param pBuf Read buffer.
+ * @param penmOsType Where to return the OS type. This is initialized to
+ * VBOXOSTYPE_Unknown.
+ */
+HRESULT Unattended::i_innerDetectIsoOSLinux(RTVFS hVfsIso, DETECTBUFFER *pBuf, VBOXOSTYPE *penmOsType)
+{
+ /*
+ * Redhat and derivatives may have a .treeinfo (ini-file style) with useful info
+ * or at least a barebone .discinfo file.
+ */
+
+ /*
+ * Start with .treeinfo: https://release-engineering.github.io/productmd/treeinfo-1.0.html
+ */
+ RTVFSFILE hVfsFile;
+ int vrc = RTVfsFileOpen(hVfsIso, ".treeinfo", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
+ if (RT_SUCCESS(vrc))
+ {
+ RTINIFILE hIniFile;
+ vrc = RTIniFileCreateFromVfsFile(&hIniFile, hVfsFile, RTINIFILE_F_READONLY);
+ RTVfsFileRelease(hVfsFile);
+ if (RT_SUCCESS(vrc))
+ {
+ /* Try figure the architecture first (like with windows). */
+ vrc = RTIniFileQueryValue(hIniFile, "tree", "arch", pBuf->sz, sizeof(*pBuf), NULL);
+ if (RT_FAILURE(vrc) || !pBuf->sz[0])
+ vrc = RTIniFileQueryValue(hIniFile, "general", "arch", pBuf->sz, sizeof(*pBuf), NULL);
+ if (RT_SUCCESS(vrc))
+ {
+ LogRelFlow(("Unattended: .treeinfo: arch=%s\n", pBuf->sz));
+ if (!detectLinuxArch(pBuf->sz, penmOsType, VBOXOSTYPE_RedHat))
+ LogRel(("Unattended: .treeinfo: Unknown: arch='%s'\n", pBuf->sz));
+ }
+ else
+ LogRel(("Unattended: .treeinfo: No 'arch' property.\n"));
+
+ /* Try figure the release name, it doesn't have to be redhat. */
+ vrc = RTIniFileQueryValue(hIniFile, "release", "name", pBuf->sz, sizeof(*pBuf), NULL);
+ if (RT_FAILURE(vrc) || !pBuf->sz[0])
+ vrc = RTIniFileQueryValue(hIniFile, "product", "name", pBuf->sz, sizeof(*pBuf), NULL);
+ if (RT_FAILURE(vrc) || !pBuf->sz[0])
+ vrc = RTIniFileQueryValue(hIniFile, "general", "family", pBuf->sz, sizeof(*pBuf), NULL);
+ if (RT_SUCCESS(vrc))
+ {
+ LogRelFlow(("Unattended: .treeinfo: name/family=%s\n", pBuf->sz));
+ if (!detectLinuxDistroName(pBuf->sz, penmOsType, NULL))
+ {
+ LogRel(("Unattended: .treeinfo: Unknown: name/family='%s', assuming Red Hat\n", pBuf->sz));
+ *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_RedHat);
+ }
+ }
+
+ /* Try figure the version. */
+ vrc = RTIniFileQueryValue(hIniFile, "release", "version", pBuf->sz, sizeof(*pBuf), NULL);
+ if (RT_FAILURE(vrc) || !pBuf->sz[0])
+ vrc = RTIniFileQueryValue(hIniFile, "product", "version", pBuf->sz, sizeof(*pBuf), NULL);
+ if (RT_FAILURE(vrc) || !pBuf->sz[0])
+ vrc = RTIniFileQueryValue(hIniFile, "general", "version", pBuf->sz, sizeof(*pBuf), NULL);
+ if (RT_SUCCESS(vrc))
+ {
+ LogRelFlow(("Unattended: .treeinfo: version=%s\n", pBuf->sz));
+ try { mStrDetectedOSVersion = RTStrStrip(pBuf->sz); }
+ catch (std::bad_alloc &) { return E_OUTOFMEMORY; }
+ }
+
+ RTIniFileRelease(hIniFile);
+ }
+
+ if (*penmOsType != VBOXOSTYPE_Unknown)
+ return S_FALSE;
+ }
+
+ /*
+ * Try .discinfo next: https://release-engineering.github.io/productmd/discinfo-1.0.html
+ * We will probably need additional info here...
+ */
+ vrc = RTVfsFileOpen(hVfsIso, ".discinfo", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
+ if (RT_SUCCESS(vrc))
+ {
+ RT_ZERO(*pBuf);
+ size_t cchIgn;
+ RTVfsFileRead(hVfsFile, pBuf->sz, sizeof(*pBuf) - 1, &cchIgn);
+ pBuf->sz[sizeof(*pBuf) - 1] = '\0';
+ RTVfsFileRelease(hVfsFile);
+
+ /* Parse and strip the first 5 lines. */
+ const char *apszLines[5];
+ char *psz = pBuf->sz;
+ for (unsigned i = 0; i < RT_ELEMENTS(apszLines); i++)
+ {
+ apszLines[i] = psz;
+ if (*psz)
+ {
+ char *pszEol = (char *)strchr(psz, '\n');
+ if (!pszEol)
+ psz = strchr(psz, '\0');
+ else
+ {
+ *pszEol = '\0';
+ apszLines[i] = RTStrStrip(psz);
+ psz = pszEol + 1;
+ }
+ }
+ }
+
+ /* Do we recognize the architecture? */
+ LogRelFlow(("Unattended: .discinfo: arch=%s\n", apszLines[2]));
+ if (!detectLinuxArch(apszLines[2], penmOsType, VBOXOSTYPE_RedHat))
+ LogRel(("Unattended: .discinfo: Unknown: arch='%s'\n", apszLines[2]));
+
+ /* Do we recognize the release string? */
+ LogRelFlow(("Unattended: .discinfo: product+version=%s\n", apszLines[1]));
+ const char *pszVersion = NULL;
+ if (!detectLinuxDistroName(apszLines[1], penmOsType, &pszVersion))
+ LogRel(("Unattended: .discinfo: Unknown: release='%s'\n", apszLines[1]));
+
+ if (*pszVersion)
+ {
+ LogRelFlow(("Unattended: .discinfo: version=%s\n", pszVersion));
+ try { mStrDetectedOSVersion = RTStrStripL(pszVersion); }
+ catch (std::bad_alloc &) { return E_OUTOFMEMORY; }
+
+ /* CentOS likes to call their release 'Final' without mentioning the actual version
+ number (e.g. CentOS-4.7-x86_64-binDVD.iso), so we need to go look elsewhere.
+ This is only important for centos 4.x and 3.x releases. */
+ if (RTStrNICmp(pszVersion, RT_STR_TUPLE("Final")) == 0)
+ {
+ static const char * const s_apszDirs[] = { "CentOS/RPMS/", "RedHat/RPMS", "Server", "Workstation" };
+ for (unsigned iDir = 0; iDir < RT_ELEMENTS(s_apszDirs); iDir++)
+ {
+ RTVFSDIR hVfsDir;
+ vrc = RTVfsDirOpen(hVfsIso, s_apszDirs[iDir], 0, &hVfsDir);
+ if (RT_FAILURE(vrc))
+ continue;
+ char szRpmDb[128];
+ char szReleaseRpm[128];
+ szRpmDb[0] = '\0';
+ szReleaseRpm[0] = '\0';
+ for (;;)
+ {
+ RTDIRENTRYEX DirEntry;
+ size_t cbDirEntry = sizeof(DirEntry);
+ vrc = RTVfsDirReadEx(hVfsDir, &DirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING);
+ if (RT_FAILURE(vrc))
+ break;
+
+ /* redhat-release-4WS-2.4.i386.rpm
+ centos-release-4-7.x86_64.rpm, centos-release-4-4.3.i386.rpm
+ centos-release-5-3.el5.centos.1.x86_64.rpm */
+ if ( (psz = strstr(DirEntry.szName, "-release-")) != NULL
+ || (psz = strstr(DirEntry.szName, "-RELEASE-")) != NULL)
+ {
+ psz += 9;
+ if (RT_C_IS_DIGIT(*psz))
+ RTStrCopy(szReleaseRpm, sizeof(szReleaseRpm), psz);
+ }
+ /* rpmdb-redhat-4WS-2.4.i386.rpm,
+ rpmdb-CentOS-4.5-0.20070506.i386.rpm,
+ rpmdb-redhat-3.9-0.20070703.i386.rpm. */
+ else if ( ( RTStrStartsWith(DirEntry.szName, "rpmdb-")
+ || RTStrStartsWith(DirEntry.szName, "RPMDB-"))
+ && RT_C_IS_DIGIT(DirEntry.szName[6]) )
+ RTStrCopy(szRpmDb, sizeof(szRpmDb), &DirEntry.szName[6]);
+ }
+ RTVfsDirRelease(hVfsDir);
+
+ /* Did we find anything relvant? */
+ psz = szRpmDb;
+ if (!RT_C_IS_DIGIT(*psz))
+ psz = szReleaseRpm;
+ if (RT_C_IS_DIGIT(*psz))
+ {
+ /* Convert '-' to '.' and strip stuff which doesn't look like a version string. */
+ char *pszCur = psz + 1;
+ for (char ch = *pszCur; ch != '\0'; ch = *++pszCur)
+ if (ch == '-')
+ *pszCur = '.';
+ else if (ch != '.' && !RT_C_IS_DIGIT(ch))
+ {
+ *pszCur = '\0';
+ break;
+ }
+ while (&pszCur[-1] != psz && pszCur[-1] == '.')
+ *--pszCur = '\0';
+
+ /* Set it and stop looking. */
+ try { mStrDetectedOSVersion = psz; }
+ catch (std::bad_alloc &) { return E_OUTOFMEMORY; }
+ break;
+ }
+ }
+ }
+ }
+
+ if (*penmOsType != VBOXOSTYPE_Unknown)
+ return S_FALSE;
+ }
+
+ return S_FALSE;
+}
+
+
+HRESULT Unattended::prepare()
+{
+ LogFlow(("Unattended::prepare: enter\n"));
+
+ /*
+ * Must have a machine.
+ */
+ ComPtr<Machine> ptrMachine;
+ Guid MachineUuid;
+ {
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ ptrMachine = mMachine;
+ if (ptrMachine.isNull())
+ return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("No machine associated with this IUnatteded instance"));
+ MachineUuid = mMachineUuid;
+ }
+
+ /*
+ * Before we write lock ourselves, we must get stuff from Machine and
+ * VirtualBox because their locks have higher priorities than ours.
+ */
+ Utf8Str strGuestOsTypeId;
+ Utf8Str strMachineName;
+ Utf8Str strDefaultAuxBasePath;
+ HRESULT hrc;
+ try
+ {
+ Bstr bstrTmp;
+ hrc = ptrMachine->COMGETTER(OSTypeId)(bstrTmp.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+ strGuestOsTypeId = bstrTmp;
+ hrc = ptrMachine->COMGETTER(Name)(bstrTmp.asOutParam());
+ if (SUCCEEDED(hrc))
+ strMachineName = bstrTmp;
+ }
+ int vrc = ptrMachine->i_calculateFullPath(Utf8StrFmt("Unattended-%RTuuid-", MachineUuid.raw()), strDefaultAuxBasePath);
+ if (RT_FAILURE(vrc))
+ return setErrorBoth(E_FAIL, vrc);
+ }
+ catch (std::bad_alloc &)
+ {
+ return E_OUTOFMEMORY;
+ }
+ bool const fIs64Bit = i_isGuestOSArchX64(strGuestOsTypeId);
+
+ BOOL fRtcUseUtc = FALSE;
+ hrc = ptrMachine->COMGETTER(RTCUseUTC)(&fRtcUseUtc);
+ if (FAILED(hrc))
+ return hrc;
+
+ /*
+ * Write lock this object and set attributes we got from IMachine.
+ */
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ mStrGuestOsTypeId = strGuestOsTypeId;
+ mfGuestOs64Bit = fIs64Bit;
+ mfRtcUseUtc = RT_BOOL(fRtcUseUtc);
+
+ /*
+ * Do some state checks.
+ */
+ if (mpInstaller != NULL)
+ return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("The prepare method has been called (must call done to restart)"));
+ if ((Machine *)ptrMachine != (Machine *)mMachine)
+ return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("The 'machine' while we were using it - please don't do that"));
+
+ /*
+ * Check if the specified ISOs and files exist.
+ */
+ if (!RTFileExists(mStrIsoPath.c_str()))
+ return setErrorBoth(E_FAIL, VERR_FILE_NOT_FOUND, tr("Could not locate the installation ISO file '%s'"),
+ mStrIsoPath.c_str());
+ if (mfInstallGuestAdditions && !RTFileExists(mStrAdditionsIsoPath.c_str()))
+ return setErrorBoth(E_FAIL, VERR_FILE_NOT_FOUND, tr("Could not locate the guest additions ISO file '%s'"),
+ mStrAdditionsIsoPath.c_str());
+ if (mfInstallTestExecService && !RTFileExists(mStrValidationKitIsoPath.c_str()))
+ return setErrorBoth(E_FAIL, VERR_FILE_NOT_FOUND, tr("Could not locate the validation kit ISO file '%s'"),
+ mStrValidationKitIsoPath.c_str());
+ if (mStrScriptTemplatePath.isNotEmpty() && !RTFileExists(mStrScriptTemplatePath.c_str()))
+ return setErrorBoth(E_FAIL, VERR_FILE_NOT_FOUND, tr("Could not locate unattended installation script template '%s'"),
+ mStrScriptTemplatePath.c_str());
+
+ /*
+ * Do media detection if it haven't been done yet.
+ */
+ if (!mfDoneDetectIsoOS)
+ {
+ hrc = detectIsoOS();
+ if (FAILED(hrc) && hrc != E_NOTIMPL)
+ return hrc;
+ }
+
+ /*
+ * Do some default property stuff and check other properties.
+ */
+ try
+ {
+ char szTmp[128];
+
+ if (mStrLocale.isEmpty())
+ {
+ int vrc = RTLocaleQueryNormalizedBaseLocaleName(szTmp, sizeof(szTmp));
+ if ( RT_SUCCESS(vrc)
+ && RTLOCALE_IS_LANGUAGE2_UNDERSCORE_COUNTRY2(szTmp))
+ mStrLocale.assign(szTmp, 5);
+ else
+ mStrLocale = "en_US";
+ Assert(RTLOCALE_IS_LANGUAGE2_UNDERSCORE_COUNTRY2(mStrLocale));
+ }
+
+ if (mStrLanguage.isEmpty())
+ {
+ if (mDetectedOSLanguages.size() > 0)
+ mStrLanguage = mDetectedOSLanguages[0];
+ else
+ mStrLanguage.assign(mStrLocale).findReplace('_', '-');
+ }
+
+ if (mStrCountry.isEmpty())
+ {
+ int vrc = RTLocaleQueryUserCountryCode(szTmp);
+ if (RT_SUCCESS(vrc))
+ mStrCountry = szTmp;
+ else if ( mStrLocale.isNotEmpty()
+ && RTLOCALE_IS_LANGUAGE2_UNDERSCORE_COUNTRY2(mStrLocale))
+ mStrCountry.assign(mStrLocale, 3, 2);
+ else
+ mStrCountry = "US";
+ }
+
+ if (mStrTimeZone.isEmpty())
+ {
+ int vrc = RTTimeZoneGetCurrent(szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc))
+ mStrTimeZone = szTmp;
+ else
+ mStrTimeZone = "Etc/UTC";
+ Assert(mStrTimeZone.isNotEmpty());
+ }
+ mpTimeZoneInfo = RTTimeZoneGetInfoByUnixName(mStrTimeZone.c_str());
+ if (!mpTimeZoneInfo)
+ mpTimeZoneInfo = RTTimeZoneGetInfoByWindowsName(mStrTimeZone.c_str());
+ Assert(mpTimeZoneInfo || mStrTimeZone != "Etc/UTC");
+ if (!mpTimeZoneInfo)
+ LogRel(("Unattended::prepare: warning: Unknown time zone '%s'\n", mStrTimeZone.c_str()));
+
+ if (mStrHostname.isEmpty())
+ {
+ /* Mangle the VM name into a valid hostname. */
+ for (size_t i = 0; i < strMachineName.length(); i++)
+ {
+ char ch = strMachineName[i];
+ if ( (unsigned)ch < 127
+ && RT_C_IS_ALNUM(ch))
+ mStrHostname.append(ch);
+ else if (mStrHostname.isNotEmpty() && RT_C_IS_PUNCT(ch) && !mStrHostname.endsWith("-"))
+ mStrHostname.append('-');
+ }
+ if (mStrHostname.length() == 0)
+ mStrHostname.printf("%RTuuid-vm", MachineUuid.raw());
+ else if (mStrHostname.length() < 3)
+ mStrHostname.append("-vm");
+ mStrHostname.append(".myguest.virtualbox.org");
+ }
+
+ if (mStrAuxiliaryBasePath.isEmpty())
+ {
+ mStrAuxiliaryBasePath = strDefaultAuxBasePath;
+ mfIsDefaultAuxiliaryBasePath = true;
+ }
+ }
+ catch (std::bad_alloc &)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ /*
+ * Get the guest OS type info and instantiate the appropriate installer.
+ */
+ uint32_t const idxOSType = Global::getOSTypeIndexFromId(mStrGuestOsTypeId.c_str());
+ meGuestOsType = idxOSType < Global::cOSTypes ? Global::sOSTypes[idxOSType].osType : VBOXOSTYPE_Unknown;
+
+ mpInstaller = UnattendedInstaller::createInstance(meGuestOsType, mStrGuestOsTypeId, mStrDetectedOSVersion,
+ mStrDetectedOSFlavor, mStrDetectedOSHints, this);
+ if (mpInstaller != NULL)
+ {
+ hrc = mpInstaller->initInstaller();
+ if (SUCCEEDED(hrc))
+ {
+ /*
+ * Do the script preps (just reads them).
+ */
+ hrc = mpInstaller->prepareUnattendedScripts();
+ if (SUCCEEDED(hrc))
+ {
+ LogFlow(("Unattended::prepare: returns S_OK\n"));
+ return S_OK;
+ }
+ }
+
+ /* Destroy the installer instance. */
+ delete mpInstaller;
+ mpInstaller = NULL;
+ }
+ else
+ hrc = setErrorBoth(E_FAIL, VERR_NOT_FOUND,
+ tr("Unattended installation is not supported for guest type '%s'"), mStrGuestOsTypeId.c_str());
+ LogRelFlow(("Unattended::prepare: failed with %Rhrc\n", hrc));
+ return hrc;
+}
+
+HRESULT Unattended::constructMedia()
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ LogFlow(("===========================================================\n"));
+ LogFlow(("Call Unattended::constructMedia()\n"));
+
+ if (mpInstaller == NULL)
+ return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, "prepare() not yet called");
+
+ return mpInstaller->prepareMedia();
+}
+
+HRESULT Unattended::reconfigureVM()
+{
+ LogFlow(("===========================================================\n"));
+ LogFlow(("Call Unattended::reconfigureVM()\n"));
+
+ /*
+ * Interrogate VirtualBox/IGuestOSType before we lock stuff and create ordering issues.
+ */
+ StorageBus_T enmRecommendedStorageBus = StorageBus_IDE;
+ {
+ Bstr bstrGuestOsTypeId;
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ bstrGuestOsTypeId = mStrGuestOsTypeId;
+ }
+ ComPtr<IGuestOSType> ptrGuestOSType;
+ HRESULT hrc = mParent->GetGuestOSType(bstrGuestOsTypeId.raw(), ptrGuestOSType.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+ if (!ptrGuestOSType.isNull())
+ hrc = ptrGuestOSType->COMGETTER(RecommendedDVDStorageBus)(&enmRecommendedStorageBus);
+ }
+ if (FAILED(hrc))
+ return hrc;
+ }
+
+ /*
+ * Take write lock (for lock order reasons, write lock our parent object too)
+ * then make sure we're the only caller of this method.
+ */
+ AutoMultiWriteLock2 alock(mMachine, this COMMA_LOCKVAL_SRC_POS);
+ HRESULT hrc;
+ if (mhThreadReconfigureVM == NIL_RTNATIVETHREAD)
+ {
+ RTNATIVETHREAD const hNativeSelf = RTThreadNativeSelf();
+ mhThreadReconfigureVM = hNativeSelf;
+
+ /*
+ * Create a new session, lock the machine and get the session machine object.
+ * Do the locking without pinning down the write locks, just to be on the safe side.
+ */
+ ComPtr<ISession> ptrSession;
+ try
+ {
+ hrc = ptrSession.createInprocObject(CLSID_Session);
+ }
+ catch (std::bad_alloc &)
+ {
+ hrc = E_OUTOFMEMORY;
+ }
+ if (SUCCEEDED(hrc))
+ {
+ alock.release();
+ hrc = mMachine->LockMachine(ptrSession, LockType_Shared);
+ alock.acquire();
+ if (SUCCEEDED(hrc))
+ {
+ ComPtr<IMachine> ptrSessionMachine;
+ hrc = ptrSession->COMGETTER(Machine)(ptrSessionMachine.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+ /*
+ * Hand the session to the inner work and let it do it job.
+ */
+ try
+ {
+ hrc = i_innerReconfigureVM(alock, enmRecommendedStorageBus, ptrSessionMachine);
+ }
+ catch (...)
+ {
+ hrc = E_UNEXPECTED;
+ }
+ }
+
+ /* Paranoia: release early in case we it a bump below. */
+ Assert(mhThreadReconfigureVM == hNativeSelf);
+ mhThreadReconfigureVM = NIL_RTNATIVETHREAD;
+
+ /*
+ * While unlocking the machine we'll have to drop the locks again.
+ */
+ alock.release();
+
+ ptrSessionMachine.setNull();
+ HRESULT hrc2 = ptrSession->UnlockMachine();
+ AssertLogRelMsg(SUCCEEDED(hrc2), ("UnlockMachine -> %Rhrc\n", hrc2));
+
+ ptrSession.setNull();
+
+ alock.acquire();
+ }
+ else
+ mhThreadReconfigureVM = NIL_RTNATIVETHREAD;
+ }
+ else
+ mhThreadReconfigureVM = NIL_RTNATIVETHREAD;
+ }
+ else
+ hrc = setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("reconfigureVM running on other thread"));
+ return hrc;
+}
+
+
+HRESULT Unattended::i_innerReconfigureVM(AutoMultiWriteLock2 &rAutoLock, StorageBus_T enmRecommendedStorageBus,
+ ComPtr<IMachine> const &rPtrSessionMachine)
+{
+ if (mpInstaller == NULL)
+ return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, "prepare() not yet called");
+
+ // Fetch all available storage controllers
+ com::SafeIfaceArray<IStorageController> arrayOfControllers;
+ HRESULT hrc = rPtrSessionMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(arrayOfControllers));
+ AssertComRCReturn(hrc, hrc);
+
+ /*
+ * Figure out where the images are to be mounted, adding controllers/ports as needed.
+ */
+ std::vector<UnattendedInstallationDisk> vecInstallationDisks;
+ if (mpInstaller->isAuxiliaryFloppyNeeded())
+ {
+ hrc = i_reconfigureFloppy(arrayOfControllers, vecInstallationDisks, rPtrSessionMachine, rAutoLock);
+ if (FAILED(hrc))
+ return hrc;
+ }
+
+ hrc = i_reconfigureIsos(arrayOfControllers, vecInstallationDisks, rPtrSessionMachine, rAutoLock, enmRecommendedStorageBus);
+ if (FAILED(hrc))
+ return hrc;
+
+ /*
+ * Mount the images.
+ */
+ for (size_t idxImage = 0; idxImage < vecInstallationDisks.size(); idxImage++)
+ {
+ UnattendedInstallationDisk const *pImage = &vecInstallationDisks.at(idxImage);
+ Assert(pImage->strImagePath.isNotEmpty());
+ hrc = i_attachImage(pImage, rPtrSessionMachine, rAutoLock);
+ if (FAILED(hrc))
+ return hrc;
+ }
+
+ /*
+ * Set the boot order.
+ *
+ * ASSUME that the HD isn't bootable when we start out, but it will be what
+ * we boot from after the first stage of the installation is done. Setting
+ * it first prevents endless reboot cylces.
+ */
+ /** @todo consider making 100% sure the disk isn't bootable (edit partition
+ * table active bits and EFI stuff). */
+ Assert( mpInstaller->getBootableDeviceType() == DeviceType_DVD
+ || mpInstaller->getBootableDeviceType() == DeviceType_Floppy);
+ hrc = rPtrSessionMachine->SetBootOrder(1, DeviceType_HardDisk);
+ if (SUCCEEDED(hrc))
+ hrc = rPtrSessionMachine->SetBootOrder(2, mpInstaller->getBootableDeviceType());
+ if (SUCCEEDED(hrc))
+ hrc = rPtrSessionMachine->SetBootOrder(3, mpInstaller->getBootableDeviceType() == DeviceType_DVD
+ ? DeviceType_Floppy : DeviceType_DVD);
+ if (FAILED(hrc))
+ return hrc;
+
+ /*
+ * Essential step.
+ *
+ * HACK ALERT! We have to release the lock here or we'll get into trouble with
+ * the VirtualBox lock (via i_saveHardware/NetworkAdaptger::i_hasDefaults/VirtualBox::i_findGuestOSType).
+ */
+ if (SUCCEEDED(hrc))
+ {
+ rAutoLock.release();
+ hrc = rPtrSessionMachine->SaveSettings();
+ rAutoLock.acquire();
+ }
+
+ return hrc;
+}
+
+/**
+ * Makes sure we've got a floppy drive attached to a floppy controller, adding
+ * the auxiliary floppy image to the installation disk vector.
+ *
+ * @returns COM status code.
+ * @param rControllers The existing controllers.
+ * @param rVecInstallatationDisks The list of image to mount.
+ * @param rPtrSessionMachine The session machine smart pointer.
+ * @param rAutoLock The lock.
+ */
+HRESULT Unattended::i_reconfigureFloppy(com::SafeIfaceArray<IStorageController> &rControllers,
+ std::vector<UnattendedInstallationDisk> &rVecInstallatationDisks,
+ ComPtr<IMachine> const &rPtrSessionMachine,
+ AutoMultiWriteLock2 &rAutoLock)
+{
+ Assert(mpInstaller->isAuxiliaryFloppyNeeded());
+
+ /*
+ * Look for a floppy controller with a primary drive (A:) we can "insert"
+ * the auxiliary floppy image. Add a controller and/or a drive if necessary.
+ */
+ bool fFoundPort0Dev0 = false;
+ Bstr bstrControllerName;
+ Utf8Str strControllerName;
+
+ for (size_t i = 0; i < rControllers.size(); ++i)
+ {
+ StorageBus_T enmStorageBus;
+ HRESULT hrc = rControllers[i]->COMGETTER(Bus)(&enmStorageBus);
+ AssertComRCReturn(hrc, hrc);
+ if (enmStorageBus == StorageBus_Floppy)
+ {
+
+ /*
+ * Found a floppy controller.
+ */
+ hrc = rControllers[i]->COMGETTER(Name)(bstrControllerName.asOutParam());
+ AssertComRCReturn(hrc, hrc);
+
+ /*
+ * Check the attchments to see if we've got a device 0 attached on port 0.
+ *
+ * While we're at it we eject flppies from all floppy drives we encounter,
+ * we don't want any confusion at boot or during installation.
+ */
+ com::SafeIfaceArray<IMediumAttachment> arrayOfMediumAttachments;
+ hrc = rPtrSessionMachine->GetMediumAttachmentsOfController(bstrControllerName.raw(),
+ ComSafeArrayAsOutParam(arrayOfMediumAttachments));
+ AssertComRCReturn(hrc, hrc);
+ strControllerName = bstrControllerName;
+ AssertLogRelReturn(strControllerName.isNotEmpty(), setErrorBoth(E_UNEXPECTED, VERR_INTERNAL_ERROR_2));
+
+ for (size_t j = 0; j < arrayOfMediumAttachments.size(); j++)
+ {
+ LONG iPort = -1;
+ hrc = arrayOfMediumAttachments[j]->COMGETTER(Port)(&iPort);
+ AssertComRCReturn(hrc, hrc);
+
+ LONG iDevice = -1;
+ hrc = arrayOfMediumAttachments[j]->COMGETTER(Device)(&iDevice);
+ AssertComRCReturn(hrc, hrc);
+
+ DeviceType_T enmType;
+ hrc = arrayOfMediumAttachments[j]->COMGETTER(Type)(&enmType);
+ AssertComRCReturn(hrc, hrc);
+
+ if (enmType == DeviceType_Floppy)
+ {
+ ComPtr<IMedium> ptrMedium;
+ hrc = arrayOfMediumAttachments[j]->COMGETTER(Medium)(ptrMedium.asOutParam());
+ AssertComRCReturn(hrc, hrc);
+
+ if (ptrMedium.isNotNull())
+ {
+ ptrMedium.setNull();
+ rAutoLock.release();
+ hrc = rPtrSessionMachine->UnmountMedium(bstrControllerName.raw(), iPort, iDevice, TRUE /*fForce*/);
+ rAutoLock.acquire();
+ }
+
+ if (iPort == 0 && iDevice == 0)
+ fFoundPort0Dev0 = true;
+ }
+ else if (iPort == 0 && iDevice == 0)
+ return setError(E_FAIL,
+ tr("Found non-floppy device attached to port 0 device 0 on the floppy controller '%ls'"),
+ bstrControllerName.raw());
+ }
+ }
+ }
+
+ /*
+ * Add a floppy controller if we need to.
+ */
+ if (strControllerName.isEmpty())
+ {
+ bstrControllerName = strControllerName = "Floppy";
+ ComPtr<IStorageController> ptrControllerIgnored;
+ HRESULT hrc = rPtrSessionMachine->AddStorageController(bstrControllerName.raw(), StorageBus_Floppy,
+ ptrControllerIgnored.asOutParam());
+ LogRelFunc(("Machine::addStorageController(Floppy) -> %Rhrc \n", hrc));
+ if (FAILED(hrc))
+ return hrc;
+ }
+
+ /*
+ * Adding a floppy drive (if needed) and mounting the auxiliary image is
+ * done later together with the ISOs.
+ */
+ rVecInstallatationDisks.push_back(UnattendedInstallationDisk(StorageBus_Floppy, strControllerName,
+ DeviceType_Floppy, AccessMode_ReadWrite,
+ 0, 0,
+ fFoundPort0Dev0 /*fMountOnly*/,
+ mpInstaller->getAuxiliaryFloppyFilePath()));
+ return S_OK;
+}
+
+/**
+ * Reconfigures DVD drives of the VM to mount all the ISOs we need.
+ *
+ * This will umount all DVD media.
+ *
+ * @returns COM status code.
+ * @param rControllers The existing controllers.
+ * @param rVecInstallatationDisks The list of image to mount.
+ * @param rPtrSessionMachine The session machine smart pointer.
+ * @param rAutoLock The lock.
+ * @param enmRecommendedStorageBus The recommended storage bus type for adding
+ * DVD drives on.
+ */
+HRESULT Unattended::i_reconfigureIsos(com::SafeIfaceArray<IStorageController> &rControllers,
+ std::vector<UnattendedInstallationDisk> &rVecInstallatationDisks,
+ ComPtr<IMachine> const &rPtrSessionMachine,
+ AutoMultiWriteLock2 &rAutoLock, StorageBus_T enmRecommendedStorageBus)
+{
+ /*
+ * Enumerate the attachements of every controller, looking for DVD drives,
+ * ASSUMEING all drives are bootable.
+ *
+ * Eject the medium from all the drives (don't want any confusion) and look
+ * for the recommended storage bus in case we need to add more drives.
+ */
+ HRESULT hrc;
+ std::list<ControllerSlot> lstControllerDvdSlots;
+ Utf8Str strRecommendedControllerName; /* non-empty if recommended bus found. */
+ Utf8Str strControllerName;
+ Bstr bstrControllerName;
+ for (size_t i = 0; i < rControllers.size(); ++i)
+ {
+ hrc = rControllers[i]->COMGETTER(Name)(bstrControllerName.asOutParam());
+ AssertComRCReturn(hrc, hrc);
+ strControllerName = bstrControllerName;
+
+ /* Look for recommended storage bus. */
+ StorageBus_T enmStorageBus;
+ hrc = rControllers[i]->COMGETTER(Bus)(&enmStorageBus);
+ AssertComRCReturn(hrc, hrc);
+ if (enmStorageBus == enmRecommendedStorageBus)
+ {
+ strRecommendedControllerName = bstrControllerName;
+ AssertLogRelReturn(strControllerName.isNotEmpty(), setErrorBoth(E_UNEXPECTED, VERR_INTERNAL_ERROR_2));
+ }
+
+ /* Scan the controller attachments. */
+ com::SafeIfaceArray<IMediumAttachment> arrayOfMediumAttachments;
+ hrc = rPtrSessionMachine->GetMediumAttachmentsOfController(bstrControllerName.raw(),
+ ComSafeArrayAsOutParam(arrayOfMediumAttachments));
+ AssertComRCReturn(hrc, hrc);
+
+ for (size_t j = 0; j < arrayOfMediumAttachments.size(); j++)
+ {
+ DeviceType_T enmType;
+ hrc = arrayOfMediumAttachments[j]->COMGETTER(Type)(&enmType);
+ AssertComRCReturn(hrc, hrc);
+ if (enmType == DeviceType_DVD)
+ {
+ LONG iPort = -1;
+ hrc = arrayOfMediumAttachments[j]->COMGETTER(Port)(&iPort);
+ AssertComRCReturn(hrc, hrc);
+
+ LONG iDevice = -1;
+ hrc = arrayOfMediumAttachments[j]->COMGETTER(Device)(&iDevice);
+ AssertComRCReturn(hrc, hrc);
+
+ /* Remeber it. */
+ lstControllerDvdSlots.push_back(ControllerSlot(enmStorageBus, strControllerName, iPort, iDevice, false /*fFree*/));
+
+ /* Eject the medium, if any. */
+ ComPtr<IMedium> ptrMedium;
+ hrc = arrayOfMediumAttachments[j]->COMGETTER(Medium)(ptrMedium.asOutParam());
+ AssertComRCReturn(hrc, hrc);
+ if (ptrMedium.isNotNull())
+ {
+ ptrMedium.setNull();
+
+ rAutoLock.release();
+ hrc = rPtrSessionMachine->UnmountMedium(bstrControllerName.raw(), iPort, iDevice, TRUE /*fForce*/);
+ rAutoLock.acquire();
+ }
+ }
+ }
+ }
+
+ /*
+ * How many drives do we need? Add more if necessary.
+ */
+ ULONG cDvdDrivesNeeded = 0;
+ if (mpInstaller->isAuxiliaryIsoNeeded())
+ cDvdDrivesNeeded++;
+ if (mpInstaller->isOriginalIsoNeeded())
+ cDvdDrivesNeeded++;
+#if 0 /* These are now in the AUX VISO. */
+ if (mpInstaller->isAdditionsIsoNeeded())
+ cDvdDrivesNeeded++;
+ if (mpInstaller->isValidationKitIsoNeeded())
+ cDvdDrivesNeeded++;
+#endif
+ Assert(cDvdDrivesNeeded > 0);
+ if (cDvdDrivesNeeded > lstControllerDvdSlots.size())
+ {
+ /* Do we need to add the recommended controller? */
+ if (strRecommendedControllerName.isEmpty())
+ {
+ switch (enmRecommendedStorageBus)
+ {
+ case StorageBus_IDE: strRecommendedControllerName = "IDE"; break;
+ case StorageBus_SATA: strRecommendedControllerName = "SATA"; break;
+ case StorageBus_SCSI: strRecommendedControllerName = "SCSI"; break;
+ case StorageBus_SAS: strRecommendedControllerName = "SAS"; break;
+ case StorageBus_USB: strRecommendedControllerName = "USB"; break;
+ case StorageBus_PCIe: strRecommendedControllerName = "PCIe"; break;
+ default:
+ return setError(E_FAIL, tr("Support for recommended storage bus %d not implemented"),
+ (int)enmRecommendedStorageBus);
+ }
+ ComPtr<IStorageController> ptrControllerIgnored;
+ hrc = rPtrSessionMachine->AddStorageController(Bstr(strRecommendedControllerName).raw(), enmRecommendedStorageBus,
+ ptrControllerIgnored.asOutParam());
+ LogRelFunc(("Machine::addStorageController(%s) -> %Rhrc \n", strRecommendedControllerName.c_str(), hrc));
+ if (FAILED(hrc))
+ return hrc;
+ }
+
+ /* Add free controller slots, maybe raising the port limit on the controller if we can. */
+ hrc = i_findOrCreateNeededFreeSlots(strRecommendedControllerName, enmRecommendedStorageBus, rPtrSessionMachine,
+ cDvdDrivesNeeded, lstControllerDvdSlots);
+ if (FAILED(hrc))
+ return hrc;
+ if (cDvdDrivesNeeded > lstControllerDvdSlots.size())
+ {
+ /* We could in many cases create another controller here, but it's not worth the effort. */
+ return setError(E_FAIL, tr("Not enough free slots on controller '%s' to add %u DVD drive(s)"),
+ strRecommendedControllerName.c_str(), cDvdDrivesNeeded - lstControllerDvdSlots.size());
+ }
+ Assert(cDvdDrivesNeeded == lstControllerDvdSlots.size());
+ }
+
+ /*
+ * Sort the DVD slots in boot order.
+ */
+ lstControllerDvdSlots.sort();
+
+ /*
+ * Prepare ISO mounts.
+ *
+ * Boot order depends on bootFromAuxiliaryIso() and we must grab DVD slots
+ * according to the boot order.
+ */
+ std::list<ControllerSlot>::const_iterator itDvdSlot = lstControllerDvdSlots.begin();
+ if (mpInstaller->isAuxiliaryIsoNeeded() && mpInstaller->bootFromAuxiliaryIso())
+ {
+ rVecInstallatationDisks.push_back(UnattendedInstallationDisk(itDvdSlot, mpInstaller->getAuxiliaryIsoFilePath()));
+ ++itDvdSlot;
+ }
+
+ if (mpInstaller->isOriginalIsoNeeded())
+ {
+ rVecInstallatationDisks.push_back(UnattendedInstallationDisk(itDvdSlot, i_getIsoPath()));
+ ++itDvdSlot;
+ }
+
+ if (mpInstaller->isAuxiliaryIsoNeeded() && !mpInstaller->bootFromAuxiliaryIso())
+ {
+ rVecInstallatationDisks.push_back(UnattendedInstallationDisk(itDvdSlot, mpInstaller->getAuxiliaryIsoFilePath()));
+ ++itDvdSlot;
+ }
+
+#if 0 /* These are now in the AUX VISO. */
+ if (mpInstaller->isAdditionsIsoNeeded())
+ {
+ rVecInstallatationDisks.push_back(UnattendedInstallationDisk(itDvdSlot, i_getAdditionsIsoPath()));
+ ++itDvdSlot;
+ }
+
+ if (mpInstaller->isValidationKitIsoNeeded())
+ {
+ rVecInstallatationDisks.push_back(UnattendedInstallationDisk(itDvdSlot, i_getValidationKitIsoPath()));
+ ++itDvdSlot;
+ }
+#endif
+
+ return S_OK;
+}
+
+/**
+ * Used to find more free slots for DVD drives during VM reconfiguration.
+ *
+ * This may modify the @a portCount property of the given controller.
+ *
+ * @returns COM status code.
+ * @param rStrControllerName The name of the controller to find/create
+ * free slots on.
+ * @param enmStorageBus The storage bus type.
+ * @param rPtrSessionMachine Reference to the session machine.
+ * @param cSlotsNeeded Total slots needed (including those we've
+ * already found).
+ * @param rDvdSlots The slot collection for DVD drives to add
+ * free slots to as we find/create them.
+ */
+HRESULT Unattended::i_findOrCreateNeededFreeSlots(const Utf8Str &rStrControllerName, StorageBus_T enmStorageBus,
+ ComPtr<IMachine> const &rPtrSessionMachine, uint32_t cSlotsNeeded,
+ std::list<ControllerSlot> &rDvdSlots)
+{
+ Assert(cSlotsNeeded > rDvdSlots.size());
+
+ /*
+ * Get controlleer stats.
+ */
+ ComPtr<IStorageController> pController;
+ HRESULT hrc = rPtrSessionMachine->GetStorageControllerByName(Bstr(rStrControllerName).raw(), pController.asOutParam());
+ AssertComRCReturn(hrc, hrc);
+
+ ULONG cMaxDevicesPerPort = 1;
+ hrc = pController->COMGETTER(MaxDevicesPerPortCount)(&cMaxDevicesPerPort);
+ AssertComRCReturn(hrc, hrc);
+ AssertLogRelReturn(cMaxDevicesPerPort > 0, E_UNEXPECTED);
+
+ ULONG cPorts = 0;
+ hrc = pController->COMGETTER(PortCount)(&cPorts);
+ AssertComRCReturn(hrc, hrc);
+
+ /*
+ * Get the attachment list and turn into an internal list for lookup speed.
+ */
+ com::SafeIfaceArray<IMediumAttachment> arrayOfMediumAttachments;
+ hrc = rPtrSessionMachine->GetMediumAttachmentsOfController(Bstr(rStrControllerName).raw(),
+ ComSafeArrayAsOutParam(arrayOfMediumAttachments));
+ AssertComRCReturn(hrc, hrc);
+
+ std::vector<ControllerSlot> arrayOfUsedSlots;
+ for (size_t i = 0; i < arrayOfMediumAttachments.size(); i++)
+ {
+ LONG iPort = -1;
+ hrc = arrayOfMediumAttachments[i]->COMGETTER(Port)(&iPort);
+ AssertComRCReturn(hrc, hrc);
+
+ LONG iDevice = -1;
+ hrc = arrayOfMediumAttachments[i]->COMGETTER(Device)(&iDevice);
+ AssertComRCReturn(hrc, hrc);
+
+ arrayOfUsedSlots.push_back(ControllerSlot(enmStorageBus, Utf8Str::Empty, iPort, iDevice, false /*fFree*/));
+ }
+
+ /*
+ * Iterate thru all possible slots, adding those not found in arrayOfUsedSlots.
+ */
+ for (uint32_t iPort = 0; iPort < cPorts; iPort++)
+ for (uint32_t iDevice = 0; iDevice < cMaxDevicesPerPort; iDevice++)
+ {
+ bool fFound = false;
+ for (size_t i = 0; i < arrayOfUsedSlots.size(); i++)
+ if ( arrayOfUsedSlots[i].uPort == iPort
+ && arrayOfUsedSlots[i].uDevice == iDevice)
+ {
+ fFound = true;
+ break;
+ }
+ if (!fFound)
+ {
+ rDvdSlots.push_back(ControllerSlot(enmStorageBus, rStrControllerName, iPort, iDevice, true /*fFree*/));
+ if (rDvdSlots.size() >= cSlotsNeeded)
+ return S_OK;
+ }
+ }
+
+ /*
+ * Okay we still need more ports. See if increasing the number of controller
+ * ports would solve it.
+ */
+ ULONG cMaxPorts = 1;
+ hrc = pController->COMGETTER(MaxPortCount)(&cMaxPorts);
+ AssertComRCReturn(hrc, hrc);
+ if (cMaxPorts <= cPorts)
+ return S_OK;
+ size_t cNewPortsNeeded = (cSlotsNeeded - rDvdSlots.size() + cMaxDevicesPerPort - 1) / cMaxDevicesPerPort;
+ if (cPorts + cNewPortsNeeded > cMaxPorts)
+ return S_OK;
+
+ /*
+ * Raise the port count and add the free slots we've just created.
+ */
+ hrc = pController->COMSETTER(PortCount)(cPorts + (ULONG)cNewPortsNeeded);
+ AssertComRCReturn(hrc, hrc);
+ for (uint32_t iPort = cPorts; iPort < cPorts + cNewPortsNeeded; iPort++)
+ for (uint32_t iDevice = 0; iDevice < cMaxDevicesPerPort; iDevice++)
+ {
+ rDvdSlots.push_back(ControllerSlot(enmStorageBus, rStrControllerName, iPort, iDevice, true /*fFree*/));
+ if (rDvdSlots.size() >= cSlotsNeeded)
+ return S_OK;
+ }
+
+ /* We should not get here! */
+ AssertLogRelFailedReturn(E_UNEXPECTED);
+}
+
+HRESULT Unattended::done()
+{
+ LogFlow(("Unattended::done\n"));
+ if (mpInstaller)
+ {
+ LogRelFlow(("Unattended::done: Deleting installer object (%p)\n", mpInstaller));
+ delete mpInstaller;
+ mpInstaller = NULL;
+ }
+ return S_OK;
+}
+
+HRESULT Unattended::getIsoPath(com::Utf8Str &isoPath)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ isoPath = mStrIsoPath;
+ return S_OK;
+}
+
+HRESULT Unattended::setIsoPath(const com::Utf8Str &isoPath)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ mStrIsoPath = isoPath;
+ mfDoneDetectIsoOS = false;
+ return S_OK;
+}
+
+HRESULT Unattended::getUser(com::Utf8Str &user)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ user = mStrUser;
+ return S_OK;
+}
+
+
+HRESULT Unattended::setUser(const com::Utf8Str &user)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ mStrUser = user;
+ return S_OK;
+}
+
+HRESULT Unattended::getPassword(com::Utf8Str &password)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ password = mStrPassword;
+ return S_OK;
+}
+
+HRESULT Unattended::setPassword(const com::Utf8Str &password)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ mStrPassword = password;
+ return S_OK;
+}
+
+HRESULT Unattended::getFullUserName(com::Utf8Str &fullUserName)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ fullUserName = mStrFullUserName;
+ return S_OK;
+}
+
+HRESULT Unattended::setFullUserName(const com::Utf8Str &fullUserName)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ mStrFullUserName = fullUserName;
+ return S_OK;
+}
+
+HRESULT Unattended::getProductKey(com::Utf8Str &productKey)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ productKey = mStrProductKey;
+ return S_OK;
+}
+
+HRESULT Unattended::setProductKey(const com::Utf8Str &productKey)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ mStrProductKey = productKey;
+ return S_OK;
+}
+
+HRESULT Unattended::getAdditionsIsoPath(com::Utf8Str &additionsIsoPath)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ additionsIsoPath = mStrAdditionsIsoPath;
+ return S_OK;
+}
+
+HRESULT Unattended::setAdditionsIsoPath(const com::Utf8Str &additionsIsoPath)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ mStrAdditionsIsoPath = additionsIsoPath;
+ return S_OK;
+}
+
+HRESULT Unattended::getInstallGuestAdditions(BOOL *installGuestAdditions)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ *installGuestAdditions = mfInstallGuestAdditions;
+ return S_OK;
+}
+
+HRESULT Unattended::setInstallGuestAdditions(BOOL installGuestAdditions)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ mfInstallGuestAdditions = installGuestAdditions != FALSE;
+ return S_OK;
+}
+
+HRESULT Unattended::getValidationKitIsoPath(com::Utf8Str &aValidationKitIsoPath)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ aValidationKitIsoPath = mStrValidationKitIsoPath;
+ return S_OK;
+}
+
+HRESULT Unattended::setValidationKitIsoPath(const com::Utf8Str &aValidationKitIsoPath)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ mStrValidationKitIsoPath = aValidationKitIsoPath;
+ return S_OK;
+}
+
+HRESULT Unattended::getInstallTestExecService(BOOL *aInstallTestExecService)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ *aInstallTestExecService = mfInstallTestExecService;
+ return S_OK;
+}
+
+HRESULT Unattended::setInstallTestExecService(BOOL aInstallTestExecService)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ mfInstallTestExecService = aInstallTestExecService != FALSE;
+ return S_OK;
+}
+
+HRESULT Unattended::getTimeZone(com::Utf8Str &aTimeZone)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ aTimeZone = mStrTimeZone;
+ return S_OK;
+}
+
+HRESULT Unattended::setTimeZone(const com::Utf8Str &aTimezone)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ mStrTimeZone = aTimezone;
+ return S_OK;
+}
+
+HRESULT Unattended::getLocale(com::Utf8Str &aLocale)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ aLocale = mStrLocale;
+ return S_OK;
+}
+
+HRESULT Unattended::setLocale(const com::Utf8Str &aLocale)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ if ( aLocale.isEmpty() /* use default */
+ || ( aLocale.length() == 5
+ && RT_C_IS_LOWER(aLocale[0])
+ && RT_C_IS_LOWER(aLocale[1])
+ && aLocale[2] == '_'
+ && RT_C_IS_UPPER(aLocale[3])
+ && RT_C_IS_UPPER(aLocale[4])) )
+ {
+ mStrLocale = aLocale;
+ return S_OK;
+ }
+ return setError(E_INVALIDARG, tr("Expected two lower cased letters, an underscore, and two upper cased letters"));
+}
+
+HRESULT Unattended::getLanguage(com::Utf8Str &aLanguage)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ aLanguage = mStrLanguage;
+ return S_OK;
+}
+
+HRESULT Unattended::setLanguage(const com::Utf8Str &aLanguage)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ mStrLanguage = aLanguage;
+ return S_OK;
+}
+
+HRESULT Unattended::getCountry(com::Utf8Str &aCountry)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ aCountry = mStrCountry;
+ return S_OK;
+}
+
+HRESULT Unattended::setCountry(const com::Utf8Str &aCountry)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ if ( aCountry.isEmpty()
+ || ( aCountry.length() == 2
+ && RT_C_IS_UPPER(aCountry[0])
+ && RT_C_IS_UPPER(aCountry[1])) )
+ {
+ mStrCountry = aCountry;
+ return S_OK;
+ }
+ return setError(E_INVALIDARG, tr("Expected two upper cased letters"));
+}
+
+HRESULT Unattended::getProxy(com::Utf8Str &aProxy)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ aProxy = ""; /// @todo turn schema map into string or something.
+ return S_OK;
+}
+
+HRESULT Unattended::setProxy(const com::Utf8Str &aProxy)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ if (aProxy.isEmpty())
+ {
+ /* set default proxy */
+ }
+ else if (aProxy.equalsIgnoreCase("none"))
+ {
+ /* clear proxy config */
+ }
+ else
+ {
+ /* Parse and set proxy config into a schema map or something along those lines. */
+ return E_NOTIMPL;
+ }
+ return S_OK;
+}
+
+HRESULT Unattended::getPackageSelectionAdjustments(com::Utf8Str &aPackageSelectionAdjustments)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ aPackageSelectionAdjustments = RTCString::join(mPackageSelectionAdjustments, ";");
+ return S_OK;
+}
+
+HRESULT Unattended::setPackageSelectionAdjustments(const com::Utf8Str &aPackageSelectionAdjustments)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ if (aPackageSelectionAdjustments.isEmpty())
+ mPackageSelectionAdjustments.clear();
+ else
+ {
+ RTCList<RTCString, RTCString *> arrayStrSplit = aPackageSelectionAdjustments.split(";");
+ for (size_t i = 0; i < arrayStrSplit.size(); i++)
+ {
+ if (arrayStrSplit[i].equals("minimal"))
+ { /* okay */ }
+ else
+ return setError(E_INVALIDARG, tr("Unknown keyword: %s"), arrayStrSplit[i].c_str());
+ }
+ mPackageSelectionAdjustments = arrayStrSplit;
+ }
+ return S_OK;
+}
+
+HRESULT Unattended::getHostname(com::Utf8Str &aHostname)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ aHostname = mStrHostname;
+ return S_OK;
+}
+
+HRESULT Unattended::setHostname(const com::Utf8Str &aHostname)
+{
+ /*
+ * Validate input.
+ */
+ if (aHostname.length() > (aHostname.endsWith(".") ? 254U : 253U))
+ return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
+ tr("Hostname '%s' is %zu bytes long, max is 253 (excluing trailing dot)"),
+ aHostname.c_str(), aHostname.length());
+ size_t cLabels = 0;
+ const char *pszSrc = aHostname.c_str();
+ for (;;)
+ {
+ size_t cchLabel = 1;
+ char ch = *pszSrc++;
+ if (RT_C_IS_ALNUM(ch))
+ {
+ cLabels++;
+ while ((ch = *pszSrc++) != '.' && ch != '\0')
+ {
+ if (RT_C_IS_ALNUM(ch) || ch == '-')
+ {
+ if (cchLabel < 63)
+ cchLabel++;
+ else
+ return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
+ tr("Invalid hostname '%s' - label %u is too long, max is 63."),
+ aHostname.c_str(), cLabels);
+ }
+ else
+ return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
+ tr("Invalid hostname '%s' - illegal char '%c' at position %zu"),
+ aHostname.c_str(), ch, pszSrc - aHostname.c_str() - 1);
+ }
+ if (cLabels == 1 && cchLabel < 2)
+ return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
+ tr("Invalid hostname '%s' - the name part must be at least two characters long"),
+ aHostname.c_str());
+ if (ch == '\0')
+ break;
+ }
+ else if (ch != '\0')
+ return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
+ tr("Invalid hostname '%s' - illegal lead char '%c' at position %zu"),
+ aHostname.c_str(), ch, pszSrc - aHostname.c_str() - 1);
+ else
+ return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
+ tr("Invalid hostname '%s' - trailing dot not permitted"), aHostname.c_str());
+ }
+ if (cLabels < 2)
+ return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
+ tr("Incomplete hostname '%s' - must include both a name and a domain"), aHostname.c_str());
+
+ /*
+ * Make the change.
+ */
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ mStrHostname = aHostname;
+ return S_OK;
+}
+
+HRESULT Unattended::getAuxiliaryBasePath(com::Utf8Str &aAuxiliaryBasePath)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ aAuxiliaryBasePath = mStrAuxiliaryBasePath;
+ return S_OK;
+}
+
+HRESULT Unattended::setAuxiliaryBasePath(const com::Utf8Str &aAuxiliaryBasePath)
+{
+ if (aAuxiliaryBasePath.isEmpty())
+ return setError(E_INVALIDARG, "Empty base path is not allowed");
+ if (!RTPathStartsWithRoot(aAuxiliaryBasePath.c_str()))
+ return setError(E_INVALIDARG, "Base path must be absolute");
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ mStrAuxiliaryBasePath = aAuxiliaryBasePath;
+ mfIsDefaultAuxiliaryBasePath = mStrAuxiliaryBasePath.isEmpty();
+ return S_OK;
+}
+
+HRESULT Unattended::getImageIndex(ULONG *index)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ *index = midxImage;
+ return S_OK;
+}
+
+HRESULT Unattended::setImageIndex(ULONG index)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ midxImage = index;
+ return S_OK;
+}
+
+HRESULT Unattended::getMachine(ComPtr<IMachine> &aMachine)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ return mMachine.queryInterfaceTo(aMachine.asOutParam());
+}
+
+HRESULT Unattended::setMachine(const ComPtr<IMachine> &aMachine)
+{
+ /*
+ * Lookup the VM so we can safely get the Machine instance.
+ * (Don't want to test how reliable XPCOM and COM are with finding
+ * the local object instance when a client passes a stub back.)
+ */
+ Bstr bstrUuidMachine;
+ HRESULT hrc = aMachine->COMGETTER(Id)(bstrUuidMachine.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+ Guid UuidMachine(bstrUuidMachine);
+ ComObjPtr<Machine> ptrMachine;
+ hrc = mParent->i_findMachine(UuidMachine, false /*fPermitInaccessible*/, true /*aSetError*/, &ptrMachine);
+ if (SUCCEEDED(hrc))
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER,
+ tr("Cannot change after prepare() has been called")));
+ mMachine = ptrMachine;
+ mMachineUuid = UuidMachine;
+ if (mfIsDefaultAuxiliaryBasePath)
+ mStrAuxiliaryBasePath.setNull();
+ hrc = S_OK;
+ }
+ }
+ return hrc;
+}
+
+HRESULT Unattended::getScriptTemplatePath(com::Utf8Str &aScriptTemplatePath)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ if ( mStrScriptTemplatePath.isNotEmpty()
+ || mpInstaller == NULL)
+ aScriptTemplatePath = mStrScriptTemplatePath;
+ else
+ aScriptTemplatePath = mpInstaller->getTemplateFilePath();
+ return S_OK;
+}
+
+HRESULT Unattended::setScriptTemplatePath(const com::Utf8Str &aScriptTemplatePath)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ mStrScriptTemplatePath = aScriptTemplatePath;
+ return S_OK;
+}
+
+HRESULT Unattended::getPostInstallScriptTemplatePath(com::Utf8Str &aPostInstallScriptTemplatePath)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ if ( mStrPostInstallScriptTemplatePath.isNotEmpty()
+ || mpInstaller == NULL)
+ aPostInstallScriptTemplatePath = mStrPostInstallScriptTemplatePath;
+ else
+ aPostInstallScriptTemplatePath = mpInstaller->getPostTemplateFilePath();
+ return S_OK;
+}
+
+HRESULT Unattended::setPostInstallScriptTemplatePath(const com::Utf8Str &aPostInstallScriptTemplatePath)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ mStrPostInstallScriptTemplatePath = aPostInstallScriptTemplatePath;
+ return S_OK;
+}
+
+HRESULT Unattended::getPostInstallCommand(com::Utf8Str &aPostInstallCommand)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ aPostInstallCommand = mStrPostInstallCommand;
+ return S_OK;
+}
+
+HRESULT Unattended::setPostInstallCommand(const com::Utf8Str &aPostInstallCommand)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ mStrPostInstallCommand = aPostInstallCommand;
+ return S_OK;
+}
+
+HRESULT Unattended::getExtraInstallKernelParameters(com::Utf8Str &aExtraInstallKernelParameters)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ if ( mStrExtraInstallKernelParameters.isNotEmpty()
+ || mpInstaller == NULL)
+ aExtraInstallKernelParameters = mStrExtraInstallKernelParameters;
+ else
+ aExtraInstallKernelParameters = mpInstaller->getDefaultExtraInstallKernelParameters();
+ return S_OK;
+}
+
+HRESULT Unattended::setExtraInstallKernelParameters(const com::Utf8Str &aExtraInstallKernelParameters)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
+ mStrExtraInstallKernelParameters = aExtraInstallKernelParameters;
+ return S_OK;
+}
+
+HRESULT Unattended::getDetectedOSTypeId(com::Utf8Str &aDetectedOSTypeId)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ aDetectedOSTypeId = mStrDetectedOSTypeId;
+ return S_OK;
+}
+
+HRESULT Unattended::getDetectedOSVersion(com::Utf8Str &aDetectedOSVersion)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ aDetectedOSVersion = mStrDetectedOSVersion;
+ return S_OK;
+}
+
+HRESULT Unattended::getDetectedOSFlavor(com::Utf8Str &aDetectedOSFlavor)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ aDetectedOSFlavor = mStrDetectedOSFlavor;
+ return S_OK;
+}
+
+HRESULT Unattended::getDetectedOSLanguages(com::Utf8Str &aDetectedOSLanguages)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ aDetectedOSLanguages = RTCString::join(mDetectedOSLanguages, " ");
+ return S_OK;
+}
+
+HRESULT Unattended::getDetectedOSHints(com::Utf8Str &aDetectedOSHints)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ aDetectedOSHints = mStrDetectedOSHints;
+ return S_OK;
+}
+
+/*
+ * Getters that the installer and script classes can use.
+ */
+Utf8Str const &Unattended::i_getIsoPath() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mStrIsoPath;
+}
+
+Utf8Str const &Unattended::i_getUser() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mStrUser;
+}
+
+Utf8Str const &Unattended::i_getPassword() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mStrPassword;
+}
+
+Utf8Str const &Unattended::i_getFullUserName() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mStrFullUserName.isNotEmpty() ? mStrFullUserName : mStrUser;
+}
+
+Utf8Str const &Unattended::i_getProductKey() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mStrProductKey;
+}
+
+Utf8Str const &Unattended::i_getAdditionsIsoPath() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mStrAdditionsIsoPath;
+}
+
+bool Unattended::i_getInstallGuestAdditions() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mfInstallGuestAdditions;
+}
+
+Utf8Str const &Unattended::i_getValidationKitIsoPath() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mStrValidationKitIsoPath;
+}
+
+bool Unattended::i_getInstallTestExecService() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mfInstallTestExecService;
+}
+
+Utf8Str const &Unattended::i_getTimeZone() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mStrTimeZone;
+}
+
+PCRTTIMEZONEINFO Unattended::i_getTimeZoneInfo() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mpTimeZoneInfo;
+}
+
+Utf8Str const &Unattended::i_getLocale() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mStrLocale;
+}
+
+Utf8Str const &Unattended::i_getLanguage() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mStrLanguage;
+}
+
+Utf8Str const &Unattended::i_getCountry() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mStrCountry;
+}
+
+bool Unattended::i_isMinimalInstallation() const
+{
+ size_t i = mPackageSelectionAdjustments.size();
+ while (i-- > 0)
+ if (mPackageSelectionAdjustments[i].equals("minimal"))
+ return true;
+ return false;
+}
+
+Utf8Str const &Unattended::i_getHostname() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mStrHostname;
+}
+
+Utf8Str const &Unattended::i_getAuxiliaryBasePath() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mStrAuxiliaryBasePath;
+}
+
+ULONG Unattended::i_getImageIndex() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return midxImage;
+}
+
+Utf8Str const &Unattended::i_getScriptTemplatePath() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mStrScriptTemplatePath;
+}
+
+Utf8Str const &Unattended::i_getPostInstallScriptTemplatePath() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mStrPostInstallScriptTemplatePath;
+}
+
+Utf8Str const &Unattended::i_getPostInstallCommand() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mStrPostInstallCommand;
+}
+
+Utf8Str const &Unattended::i_getExtraInstallKernelParameters() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mStrExtraInstallKernelParameters;
+}
+
+bool Unattended::i_isRtcUsingUtc() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mfRtcUseUtc;
+}
+
+bool Unattended::i_isGuestOs64Bit() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return mfGuestOs64Bit;
+}
+
+VBOXOSTYPE Unattended::i_getGuestOsType() const
+{
+ Assert(isReadLockedOnCurrentThread());
+ return meGuestOsType;
+}
+
+HRESULT Unattended::i_attachImage(UnattendedInstallationDisk const *pImage, ComPtr<IMachine> const &rPtrSessionMachine,
+ AutoMultiWriteLock2 &rLock)
+{
+ /*
+ * Attach the disk image
+ * HACK ALERT! Temporarily release the Unattended lock.
+ */
+ rLock.release();
+
+ ComPtr<IMedium> ptrMedium;
+ HRESULT rc = mParent->OpenMedium(Bstr(pImage->strImagePath).raw(),
+ pImage->enmDeviceType,
+ pImage->enmAccessType,
+ true,
+ ptrMedium.asOutParam());
+ LogRelFlowFunc(("VirtualBox::openMedium -> %Rhrc\n", rc));
+ if (SUCCEEDED(rc))
+ {
+ if (pImage->fMountOnly)
+ {
+ // mount the opened disk image
+ rc = rPtrSessionMachine->MountMedium(Bstr(pImage->strControllerName).raw(), pImage->uPort,
+ pImage->uDevice, ptrMedium, TRUE /*fForce*/);
+ LogRelFlowFunc(("Machine::MountMedium -> %Rhrc\n", rc));
+ }
+ else
+ {
+ //attach the opened disk image to the controller
+ rc = rPtrSessionMachine->AttachDevice(Bstr(pImage->strControllerName).raw(), pImage->uPort,
+ pImage->uDevice, pImage->enmDeviceType, ptrMedium);
+ LogRelFlowFunc(("Machine::AttachDevice -> %Rhrc\n", rc));
+ }
+ }
+
+ rLock.acquire();
+ return rc;
+}
+
+bool Unattended::i_isGuestOSArchX64(Utf8Str const &rStrGuestOsTypeId)
+{
+ ComPtr<IGuestOSType> pGuestOSType;
+ HRESULT hrc = mParent->GetGuestOSType(Bstr(rStrGuestOsTypeId).raw(), pGuestOSType.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+ BOOL fIs64Bit = FALSE;
+ if (!pGuestOSType.isNull())
+ hrc = pGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit);
+ if (SUCCEEDED(hrc))
+ return fIs64Bit != FALSE;
+ }
+ return false;
+}
+