VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/UnattendedImpl.cpp@ 93536

最後變更 在這個檔案從93536是 93536,由 vboxsync 提交於 3 年 前

Main/Unattended: bugref:9781. Build fix.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 135.0 KB
 
1/* $Id: UnattendedImpl.cpp 93536 2022-02-01 17:02:58Z vboxsync $ */
2/** @file
3 * Unattended class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_MAIN_UNATTENDED
23#include "LoggingNew.h"
24#include "VirtualBoxBase.h"
25#include "UnattendedImpl.h"
26#include "UnattendedInstaller.h"
27#include "UnattendedScript.h"
28#include "VirtualBoxImpl.h"
29#include "SystemPropertiesImpl.h"
30#include "MachineImpl.h"
31#include "Global.h"
32#include "StringifyEnums.h"
33
34#include <VBox/err.h>
35#include <iprt/cpp/xml.h>
36#include <iprt/ctype.h>
37#include <iprt/file.h>
38#include <iprt/formats/wim.h>
39#include <iprt/fsvfs.h>
40#include <iprt/inifile.h>
41#include <iprt/locale.h>
42#include <iprt/path.h>
43#include <iprt/vfs.h>
44
45using namespace std;
46
47
48/*********************************************************************************************************************************
49* Structures and Typedefs *
50*********************************************************************************************************************************/
51/**
52 * Controller slot for a DVD drive.
53 *
54 * The slot can be free and needing a drive to be attached along with the ISO
55 * image, or it may already be there and only need mounting the ISO. The
56 * ControllerSlot::fFree member indicates which it is.
57 */
58struct ControllerSlot
59{
60 StorageBus_T enmBus;
61 Utf8Str strControllerName;
62 LONG iPort;
63 LONG iDevice;
64 bool fFree;
65
66 ControllerSlot(StorageBus_T a_enmBus, const Utf8Str &a_rName, LONG a_iPort, LONG a_iDevice, bool a_fFree)
67 : enmBus(a_enmBus), strControllerName(a_rName), iPort(a_iPort), iDevice(a_iDevice), fFree(a_fFree)
68 {}
69
70 bool operator<(const ControllerSlot &rThat) const
71 {
72 if (enmBus == rThat.enmBus)
73 {
74 if (strControllerName == rThat.strControllerName)
75 {
76 if (iPort == rThat.iPort)
77 return iDevice < rThat.iDevice;
78 return iPort < rThat.iPort;
79 }
80 return strControllerName < rThat.strControllerName;
81 }
82
83 /*
84 * Bus comparsion in boot priority order.
85 */
86 /* IDE first. */
87 if (enmBus == StorageBus_IDE)
88 return true;
89 if (rThat.enmBus == StorageBus_IDE)
90 return false;
91 /* SATA next */
92 if (enmBus == StorageBus_SATA)
93 return true;
94 if (rThat.enmBus == StorageBus_SATA)
95 return false;
96 /* SCSI next */
97 if (enmBus == StorageBus_SCSI)
98 return true;
99 if (rThat.enmBus == StorageBus_SCSI)
100 return false;
101 /* numerical */
102 return (int)enmBus < (int)rThat.enmBus;
103 }
104
105 bool operator==(const ControllerSlot &rThat) const
106 {
107 return enmBus == rThat.enmBus
108 && strControllerName == rThat.strControllerName
109 && iPort == rThat.iPort
110 && iDevice == rThat.iDevice;
111 }
112};
113
114/**
115 * Installation disk.
116 *
117 * Used when reconfiguring the VM.
118 */
119typedef struct UnattendedInstallationDisk
120{
121 StorageBus_T enmBusType; /**< @todo nobody is using this... */
122 Utf8Str strControllerName;
123 DeviceType_T enmDeviceType;
124 AccessMode_T enmAccessType;
125 LONG iPort;
126 LONG iDevice;
127 bool fMountOnly;
128 Utf8Str strImagePath;
129
130 UnattendedInstallationDisk(StorageBus_T a_enmBusType, Utf8Str const &a_rBusName, DeviceType_T a_enmDeviceType,
131 AccessMode_T a_enmAccessType, LONG a_iPort, LONG a_iDevice, bool a_fMountOnly,
132 Utf8Str const &a_rImagePath)
133 : enmBusType(a_enmBusType), strControllerName(a_rBusName), enmDeviceType(a_enmDeviceType), enmAccessType(a_enmAccessType)
134 , iPort(a_iPort), iDevice(a_iDevice), fMountOnly(a_fMountOnly), strImagePath(a_rImagePath)
135 {
136 Assert(strControllerName.length() > 0);
137 }
138
139 UnattendedInstallationDisk(std::list<ControllerSlot>::const_iterator const &itDvdSlot, Utf8Str const &a_rImagePath)
140 : enmBusType(itDvdSlot->enmBus), strControllerName(itDvdSlot->strControllerName), enmDeviceType(DeviceType_DVD)
141 , enmAccessType(AccessMode_ReadOnly), iPort(itDvdSlot->iPort), iDevice(itDvdSlot->iDevice)
142 , fMountOnly(!itDvdSlot->fFree), strImagePath(a_rImagePath)
143 {
144 Assert(strControllerName.length() > 0);
145 }
146} UnattendedInstallationDisk;
147
148
149/**
150 * OS/2 syslevel file header.
151 */
152#pragma pack(1)
153typedef struct OS2SYSLEVELHDR
154{
155 uint16_t uMinusOne; /**< 0x00: UINT16_MAX */
156 char achSignature[8]; /**< 0x02: "SYSLEVEL" */
157 uint8_t abReserved1[5]; /**< 0x0a: Usually zero. Ignore. */
158 uint16_t uSyslevelFileVer; /**< 0x0f: The syslevel file version: 1. */
159 uint8_t abReserved2[16]; /**< 0x11: Zero. Ignore. */
160 uint32_t offTable; /**< 0x21: Offset of the syslevel table. */
161} OS2SYSLEVELHDR;
162#pragma pack()
163AssertCompileSize(OS2SYSLEVELHDR, 0x25);
164
165/**
166 * OS/2 syslevel table entry.
167 */
168#pragma pack(1)
169typedef struct OS2SYSLEVELENTRY
170{
171 uint16_t id; /**< 0x00: ? */
172 uint8_t bEdition; /**< 0x02: The OS/2 edition: 0=standard, 1=extended, x=component defined */
173 uint8_t bVersion; /**< 0x03: 0x45 = 4.5 */
174 uint8_t bModify; /**< 0x04: Lower nibble is added to bVersion, so 0x45 0x02 => 4.52 */
175 uint8_t abReserved1[2]; /**< 0x05: Zero. Ignore. */
176 char achCsdLevel[8]; /**< 0x07: The current CSD level. */
177 char achCsdPrior[8]; /**< 0x0f: The prior CSD level. */
178 char szName[80]; /**< 0x5f: System/component name. */
179 char achId[9]; /**< 0x67: System/component ID. */
180 uint8_t bRefresh; /**< 0x70: Single digit refresh version, ignored if zero. */
181 char szType[9]; /**< 0x71: Some kind of type string. Optional */
182 uint8_t abReserved2[6]; /**< 0x7a: Zero. Ignore. */
183} OS2SYSLEVELENTRY;
184#pragma pack()
185AssertCompileSize(OS2SYSLEVELENTRY, 0x80);
186
187/**
188 * Concatenate image name and version strings and return.
189 * A possible output would be Windows 10 Home 10-0-19041
190 *
191 * @returns Concatenated name and version strings
192 */
193Utf8Str WIMImage::getNameAndVersion() const
194{
195 Utf8Str strNameAndVersion(mName);
196 /* Append the major version number if it is not empty. */
197 if (mVersionMajor.isEmpty())
198 return strNameAndVersion;
199 strNameAndVersion.appendPrintfNoThrow(" %s", mVersionMajor.c_str());
200 /* Same for the minor version number and build number. */
201 if (mVersionMinor.isEmpty())
202 return strNameAndVersion;
203 strNameAndVersion.appendPrintfNoThrow("-%s", mVersionMinor.c_str());
204 if (mVersionBuild.isEmpty())
205 return strNameAndVersion;
206 strNameAndVersion.appendPrintfNoThrow("-%s", mVersionBuild.c_str());
207 return strNameAndVersion;
208}
209
210//////////////////////////////////////////////////////////////////////////////////////////////////////
211/*
212*
213*
214* Implementation Unattended functions
215*
216*/
217//////////////////////////////////////////////////////////////////////////////////////////////////////
218
219Unattended::Unattended()
220 : mhThreadReconfigureVM(NIL_RTNATIVETHREAD), mfRtcUseUtc(false), mfGuestOs64Bit(false)
221 , mpInstaller(NULL), mpTimeZoneInfo(NULL), mfIsDefaultAuxiliaryBasePath(true), mfDoneDetectIsoOS(false)
222{ }
223
224Unattended::~Unattended()
225{
226 if (mpInstaller)
227 {
228 delete mpInstaller;
229 mpInstaller = NULL;
230 }
231}
232
233HRESULT Unattended::FinalConstruct()
234{
235 return BaseFinalConstruct();
236}
237
238void Unattended::FinalRelease()
239{
240 uninit();
241
242 BaseFinalRelease();
243}
244
245void Unattended::uninit()
246{
247 /* Enclose the state transition Ready->InUninit->NotReady */
248 AutoUninitSpan autoUninitSpan(this);
249 if (autoUninitSpan.uninitDone())
250 return;
251
252 unconst(mParent) = NULL;
253 mMachine.setNull();
254}
255
256/**
257 * Initializes the unattended object.
258 *
259 * @param aParent Pointer to the parent object.
260 */
261HRESULT Unattended::initUnattended(VirtualBox *aParent)
262{
263 LogFlowThisFunc(("aParent=%p\n", aParent));
264 ComAssertRet(aParent, E_INVALIDARG);
265
266 /* Enclose the state transition NotReady->InInit->Ready */
267 AutoInitSpan autoInitSpan(this);
268 AssertReturn(autoInitSpan.isOk(), E_FAIL);
269
270 unconst(mParent) = aParent;
271
272 /*
273 * Fill public attributes (IUnattended) with useful defaults.
274 */
275 try
276 {
277 mStrUser = "vboxuser";
278 mStrPassword = "changeme";
279 mfInstallGuestAdditions = false;
280 mfInstallTestExecService = false;
281 midxImage = 1;
282
283 HRESULT hrc = mParent->i_getSystemProperties()->i_getDefaultAdditionsISO(mStrAdditionsIsoPath);
284 ComAssertComRCRet(hrc, hrc);
285 }
286 catch (std::bad_alloc &)
287 {
288 return E_OUTOFMEMORY;
289 }
290
291 /*
292 * Confirm a successful initialization
293 */
294 autoInitSpan.setSucceeded();
295
296 return S_OK;
297}
298
299HRESULT Unattended::detectIsoOS()
300{
301 HRESULT hrc;
302 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
303
304/** @todo once UDF is implemented properly and we've tested this code a lot
305 * more, replace E_NOTIMPL with E_FAIL. */
306
307
308 /*
309 * Open the ISO.
310 */
311 RTVFSFILE hVfsFileIso;
312 int vrc = RTVfsFileOpenNormal(mStrIsoPath.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFileIso);
313 if (RT_FAILURE(vrc))
314 return setErrorBoth(E_NOTIMPL, vrc, tr("Failed to open '%s' (%Rrc)"), mStrIsoPath.c_str(), vrc);
315
316 RTERRINFOSTATIC ErrInfo;
317 RTVFS hVfsIso;
318 vrc = RTFsIso9660VolOpen(hVfsFileIso, 0 /*fFlags*/, &hVfsIso, RTErrInfoInitStatic(&ErrInfo));
319 if (RT_SUCCESS(vrc))
320 {
321 /*
322 * Try do the detection. Repeat for different file system variations (nojoliet, noudf).
323 */
324 hrc = i_innerDetectIsoOS(hVfsIso);
325
326 RTVfsRelease(hVfsIso);
327 if (hrc != S_FALSE) /** @todo Finish the linux and windows detection code. Only OS/2 returns S_OK right now. */
328 hrc = E_NOTIMPL;
329 }
330 else if (RTErrInfoIsSet(&ErrInfo.Core))
331 hrc = setErrorBoth(E_NOTIMPL, vrc, tr("Failed to open '%s' as ISO FS (%Rrc) - %s"),
332 mStrIsoPath.c_str(), vrc, ErrInfo.Core.pszMsg);
333 else
334 hrc = setErrorBoth(E_NOTIMPL, vrc, tr("Failed to open '%s' as ISO FS (%Rrc)"), mStrIsoPath.c_str(), vrc);
335 RTVfsFileRelease(hVfsFileIso);
336
337 /*
338 * Just fake up some windows installation media locale (for <UILanguage>).
339 * Note! The translation here isn't perfect. Feel free to send us a patch.
340 */
341 if (mDetectedOSLanguages.size() == 0)
342 {
343 char szTmp[16];
344 const char *pszFilename = RTPathFilename(mStrIsoPath.c_str());
345 if ( pszFilename
346 && RT_C_IS_ALPHA(pszFilename[0])
347 && RT_C_IS_ALPHA(pszFilename[1])
348 && (pszFilename[2] == '-' || pszFilename[2] == '_') )
349 {
350 szTmp[0] = (char)RT_C_TO_LOWER(pszFilename[0]);
351 szTmp[1] = (char)RT_C_TO_LOWER(pszFilename[1]);
352 szTmp[2] = '-';
353 if (szTmp[0] == 'e' && szTmp[1] == 'n')
354 strcpy(&szTmp[3], "US");
355 else if (szTmp[0] == 'a' && szTmp[1] == 'r')
356 strcpy(&szTmp[3], "SA");
357 else if (szTmp[0] == 'd' && szTmp[1] == 'a')
358 strcpy(&szTmp[3], "DK");
359 else if (szTmp[0] == 'e' && szTmp[1] == 't')
360 strcpy(&szTmp[3], "EE");
361 else if (szTmp[0] == 'e' && szTmp[1] == 'l')
362 strcpy(&szTmp[3], "GR");
363 else if (szTmp[0] == 'h' && szTmp[1] == 'e')
364 strcpy(&szTmp[3], "IL");
365 else if (szTmp[0] == 'j' && szTmp[1] == 'a')
366 strcpy(&szTmp[3], "JP");
367 else if (szTmp[0] == 's' && szTmp[1] == 'v')
368 strcpy(&szTmp[3], "SE");
369 else if (szTmp[0] == 'u' && szTmp[1] == 'k')
370 strcpy(&szTmp[3], "UA");
371 else if (szTmp[0] == 'c' && szTmp[1] == 's')
372 strcpy(szTmp, "cs-CZ");
373 else if (szTmp[0] == 'n' && szTmp[1] == 'o')
374 strcpy(szTmp, "nb-NO");
375 else if (szTmp[0] == 'p' && szTmp[1] == 'p')
376 strcpy(szTmp, "pt-PT");
377 else if (szTmp[0] == 'p' && szTmp[1] == 't')
378 strcpy(szTmp, "pt-BR");
379 else if (szTmp[0] == 'c' && szTmp[1] == 'n')
380 strcpy(szTmp, "zh-CN");
381 else if (szTmp[0] == 'h' && szTmp[1] == 'k')
382 strcpy(szTmp, "zh-HK");
383 else if (szTmp[0] == 't' && szTmp[1] == 'w')
384 strcpy(szTmp, "zh-TW");
385 else if (szTmp[0] == 's' && szTmp[1] == 'r')
386 strcpy(szTmp, "sr-Latn-CS"); /* hmm */
387 else
388 {
389 szTmp[3] = (char)RT_C_TO_UPPER(pszFilename[0]);
390 szTmp[4] = (char)RT_C_TO_UPPER(pszFilename[1]);
391 szTmp[5] = '\0';
392 }
393 }
394 else
395 strcpy(szTmp, "en-US");
396 try
397 {
398 mDetectedOSLanguages.append(szTmp);
399 }
400 catch (std::bad_alloc &)
401 {
402 return E_OUTOFMEMORY;
403 }
404 }
405
406 /** @todo implement actual detection logic. */
407 return hrc;
408}
409
410HRESULT Unattended::i_innerDetectIsoOS(RTVFS hVfsIso)
411{
412 DETECTBUFFER uBuf;
413 VBOXOSTYPE enmOsType = VBOXOSTYPE_Unknown;
414 HRESULT hrc = i_innerDetectIsoOSWindows(hVfsIso, &uBuf, &enmOsType);
415 if (hrc == S_FALSE && enmOsType == VBOXOSTYPE_Unknown)
416 hrc = i_innerDetectIsoOSLinux(hVfsIso, &uBuf, &enmOsType);
417 if (hrc == S_FALSE && enmOsType == VBOXOSTYPE_Unknown)
418 hrc = i_innerDetectIsoOSOs2(hVfsIso, &uBuf, &enmOsType);
419 if (enmOsType != VBOXOSTYPE_Unknown)
420 {
421 try { mStrDetectedOSTypeId = Global::OSTypeId(enmOsType); }
422 catch (std::bad_alloc &) { hrc = E_OUTOFMEMORY; }
423 }
424 return hrc;
425}
426
427/**
428 * Parses XML Node assuming a structure as follows
429 * @verbatim
430 * <VERSION>
431 * <MAJOR>10</MAJOR>
432 * <MINOR>0</MINOR>
433 * <BUILD>19041</BUILD>
434 * ......
435 * </VERSION>
436 * @endverbatim
437 * @param pNode Points to the vesion XML node,
438 * @param image Out reference to an WIMImage instance.
439 */
440static void parseVersionElement(const xml::ElementNode *pNode, WIMImage &image)
441{
442 /* Major part. */
443 const ElementNode *pMajorNode = pNode->findChildElement("MAJOR");
444 if (!pMajorNode)
445 pMajorNode = pNode->findChildElement("major");
446 if (pMajorNode)
447 image.mVersionMajor = pMajorNode->getValue();
448
449 /* Minor part. */
450 const ElementNode *pMinorNode = pNode->findChildElement("MINOR");
451 if (!pMinorNode)
452 pMinorNode = pNode->findChildElement("minor");
453 if (pMinorNode)
454 image.mVersionMinor = pMinorNode->getValue();
455
456 /* Build part. */
457 const ElementNode *pBuildNode = pNode->findChildElement("BUILD");
458 if (!pBuildNode)
459 pBuildNode = pNode->findChildElement("build");
460 if (pBuildNode)
461 image.mVersionBuild = pBuildNode->getValue();
462}
463
464
465/**
466 * Parses XML tree assuming th following structure
467 * @verbatim
468 * <WIM>
469 * ....
470 * <IMAGE INDEX="1">
471 * ....
472 * <DISPLAYNAME>Windows 10 Home</DISPLAYNAME>
473 * <VERSION>
474 * ....
475 * </VERSION>
476 * </IMAGE>
477 * </WIM>
478 * @endverbatim
479 *
480 * @param pElmRoot Pointer to the root node of the tree,
481 * @param imageList Detected images are appended to this list.
482 */
483static void parseWimXMLData(const xml::ElementNode *pElmRoot, RTCList<WIMImage> &imageList)
484{
485 if (!pElmRoot)
486 return;
487
488 ElementNodesList children;
489 int cChildren = pElmRoot->getChildElements(children, "IMAGE");
490 if (cChildren == 0)
491 cChildren = pElmRoot->getChildElements(children, "image");
492
493 for (ElementNodesList::iterator iterator = children.begin(); iterator != children.end(); ++iterator)
494 {
495 const ElementNode *pChild = *(iterator);
496 if (!pChild)
497 continue;
498
499 const char *pszIndex = pChild->findAttributeValue("INDEX");
500 if (!pszIndex)
501 pszIndex = pChild->findAttributeValue("index");
502 if (!pszIndex)
503 continue;
504 uint32_t pu32 = 0;
505 int vrc = RTStrToUInt32Full(pszIndex, 10 /* uBase */, &pu32);
506
507 if (!RT_SUCCESS(vrc))
508 continue;
509
510 const ElementNode *pDisplayDescriptionNode = pChild->findChildElement("DISPLAYNAME");
511 if (!pDisplayDescriptionNode)
512 pDisplayDescriptionNode = pChild->findChildElement("displayname");
513 if (!pDisplayDescriptionNode)
514 continue;
515 WIMImage newImage;
516 newImage.mName = pDisplayDescriptionNode->getValue();
517 if (newImage.mName.isEmpty())
518 continue;
519
520 newImage.mImageIndex = pu32;
521 const ElementNode *pVersionElement = pChild->findChildElement("VERSION");
522 if (!pVersionElement)
523 pVersionElement = pChild->findChildElement("version");
524 if (pVersionElement)
525 parseVersionElement(pVersionElement, newImage);
526 imageList.append(newImage);
527 }
528}
529
530/**
531 * Detect Windows ISOs.
532 *
533 * @returns COM status code.
534 * @retval S_OK if detected
535 * @retval S_FALSE if not fully detected.
536 *
537 * @param hVfsIso The ISO file system.
538 * @param pBuf Read buffer.
539 * @param penmOsType Where to return the OS type. This is initialized to
540 * VBOXOSTYPE_Unknown.
541 */
542HRESULT Unattended::i_innerDetectIsoOSWindows(RTVFS hVfsIso, DETECTBUFFER *pBuf, VBOXOSTYPE *penmOsType)
543{
544 /** @todo The 'sources/' path can differ. */
545
546 // globalinstallorder.xml - vista beta2
547 // sources/idwbinfo.txt - ditto.
548 // sources/lang.ini - ditto.
549
550 RTVFSFILE hVfsFile;
551 /** @todo look at the install.wim file too, extracting the XML (easy) and
552 * figure out the available image numbers and such. The format is
553 * documented. It would also provide really accurate Windows
554 * version information without the need to guess. The current
555 * content of mStrDetectedOSVersion is mostly useful for human
556 * consumption. ~~Long term it should be possible to have version
557 * conditionals (expr style, please) in the templates, which
558 * would make them a lot easier to write and more flexible at the
559 * same time. - done already~~
560 *
561 * Here is how to list images inside an install.wim file from powershell:
562 * https://docs.microsoft.com/en-us/powershell/module/dism/get-windowsimage?view=windowsserver2022-ps
563 *
564 * Unfortunately, powershell is not available by default on non-windows hosts, so we
565 * have to do it ourselves of course, but this can help when coding & testing.
566 */
567
568 int vrc = RTVfsFileOpen(hVfsIso, "sources/install.wim", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
569 if (RT_SUCCESS(vrc))
570 {
571 WIMHEADERV1 header;
572 size_t cbRead = 0;
573 vrc = RTVfsFileRead(hVfsFile, &header, sizeof(header), &cbRead);
574 if (RT_SUCCESS(vrc) && cbRead == sizeof(header))
575 {
576 /* If the xml data is not compressed, xml data is not empty, and not too big. */
577 if ( (header.XmlData.bFlags & RESHDR_FLAGS_METADATA)
578 && !(header.XmlData.bFlags & RESHDR_FLAGS_COMPRESSED)
579 && header.XmlData.cbOrginal != 0
580 && header.XmlData.cbOrginal < _32M
581 && header.XmlData.cbOrginal == header.XmlData.cb)
582 {
583 char *pXmlBuf = (char*)RTMemAlloc(header.XmlData.cbOrginal);
584 if (pXmlBuf)
585 {
586 vrc = RTVfsFileReadAt(hVfsFile, (RTFOFF)header.XmlData.off, pXmlBuf, (size_t)header.XmlData.cbOrginal, NULL);
587 if (RT_SUCCESS(vrc))
588 {
589 xml::Document doc;
590 xml::XmlMemParser parser;
591 RTCString fileName = "dump";
592 try
593 {
594 parser.read(pXmlBuf, header.XmlData.cbOrginal, fileName, doc);
595 }
596 catch (xml::XmlError &rErr)
597 {
598 LogRel(("Unattended: An error has occured during XML parsing: %s\n", rErr.what()));
599 vrc = VERR_XAR_TOC_XML_PARSE_ERROR;
600 }
601 catch (...)
602 {
603 LogRel(("Unattended: An unknown error has occured during XML parsing.\n"));
604 vrc = VERR_UNEXPECTED_EXCEPTION;
605 }
606 if (RT_SUCCESS(vrc))
607 {
608 xml::ElementNode *pElmRoot = doc.getRootElement();
609 if (pElmRoot)
610 {
611 mDetectedImages.clear();
612 parseWimXMLData(pElmRoot, mDetectedImages);
613 }
614 else
615 LogRel(("Unattended: No root element found in XML Metadata of install.wim\n"));
616 }
617 }
618 else
619 LogRel(("Unattended: Failed during reading XML Metadata out of install.wim\n"));
620 RTMemFree(pXmlBuf);
621 }
622 }
623 else
624 LogRel(("Unattended: XML Metadata of install.wim is either compressed, empty, or too big\n"));
625 }
626 RTVfsFileRelease(hVfsFile);
627 }
628
629 const char *pszVersion = NULL;
630 const char *pszProduct = NULL;
631 /*
632 * Try look for the 'sources/idwbinfo.txt' file containing windows build info.
633 * This file appeared with Vista beta 2 from what we can tell. Before windows 10
634 * it contains easily decodable branch names, after that things goes weird.
635 */
636 vrc = RTVfsFileOpen(hVfsIso, "sources/idwbinfo.txt", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
637 if (RT_SUCCESS(vrc))
638 {
639 *penmOsType = VBOXOSTYPE_WinNT_x64;
640
641 RTINIFILE hIniFile;
642 vrc = RTIniFileCreateFromVfsFile(&hIniFile, hVfsFile, RTINIFILE_F_READONLY);
643 RTVfsFileRelease(hVfsFile);
644 if (RT_SUCCESS(vrc))
645 {
646 vrc = RTIniFileQueryValue(hIniFile, "BUILDINFO", "BuildArch", pBuf->sz, sizeof(*pBuf), NULL);
647 if (RT_SUCCESS(vrc))
648 {
649 LogRelFlow(("Unattended: sources/idwbinfo.txt: BuildArch=%s\n", pBuf->sz));
650 if ( RTStrNICmp(pBuf->sz, RT_STR_TUPLE("amd64")) == 0
651 || RTStrNICmp(pBuf->sz, RT_STR_TUPLE("x64")) == 0 /* just in case */ )
652 *penmOsType = VBOXOSTYPE_WinNT_x64;
653 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("x86")) == 0)
654 *penmOsType = VBOXOSTYPE_WinNT;
655 else
656 {
657 LogRel(("Unattended: sources/idwbinfo.txt: Unknown: BuildArch=%s\n", pBuf->sz));
658 *penmOsType = VBOXOSTYPE_WinNT_x64;
659 }
660 }
661
662 vrc = RTIniFileQueryValue(hIniFile, "BUILDINFO", "BuildBranch", pBuf->sz, sizeof(*pBuf), NULL);
663 if (RT_SUCCESS(vrc))
664 {
665 LogRelFlow(("Unattended: sources/idwbinfo.txt: BuildBranch=%s\n", pBuf->sz));
666 if ( RTStrNICmp(pBuf->sz, RT_STR_TUPLE("vista")) == 0
667 || RTStrNICmp(pBuf->sz, RT_STR_TUPLE("winmain_beta")) == 0)
668 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_WinVista);
669 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("lh_sp2rtm")) == 0)
670 {
671 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_WinVista);
672 pszVersion = "sp2";
673 }
674 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("longhorn_rtm")) == 0)
675 {
676 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_WinVista);
677 pszVersion = "sp1";
678 }
679 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("win7")) == 0)
680 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win7);
681 else if ( RTStrNICmp(pBuf->sz, RT_STR_TUPLE("winblue")) == 0
682 || RTStrNICmp(pBuf->sz, RT_STR_TUPLE("winmain_blue")) == 0
683 || RTStrNICmp(pBuf->sz, RT_STR_TUPLE("win81")) == 0 /* not seen, but just in case its out there */ )
684 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win81);
685 else if ( RTStrNICmp(pBuf->sz, RT_STR_TUPLE("win8")) == 0
686 || RTStrNICmp(pBuf->sz, RT_STR_TUPLE("winmain_win8")) == 0 )
687 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win8);
688 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("th1")) == 0)
689 {
690 pszVersion = "1507"; // aka. GA, retroactively 1507
691 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
692 }
693 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("th2")) == 0)
694 {
695 pszVersion = "1511"; // aka. threshold 2
696 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
697 }
698 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("rs1_release")) == 0)
699 {
700 pszVersion = "1607"; // aka. anniversay update; rs=redstone
701 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
702 }
703 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("rs2_release")) == 0)
704 {
705 pszVersion = "1703"; // aka. creators update
706 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
707 }
708 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("rs3_release")) == 0)
709 {
710 pszVersion = "1709"; // aka. fall creators update
711 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
712 }
713 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("rs4_release")) == 0)
714 {
715 pszVersion = "1803";
716 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
717 }
718 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("rs5_release")) == 0)
719 {
720 pszVersion = "1809";
721 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
722 }
723 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("19h1_release")) == 0)
724 {
725 pszVersion = "1903";
726 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
727 }
728 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("19h2_release")) == 0)
729 {
730 pszVersion = "1909"; // ??
731 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
732 }
733 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("20h1_release")) == 0)
734 {
735 pszVersion = "2003"; // ??
736 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
737 }
738 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("vb_release")) == 0)
739 {
740 pszVersion = "2004"; // ?? vb=Vibranium
741 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
742 }
743 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("20h2_release")) == 0)
744 {
745 pszVersion = "2009"; // ??
746 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
747 }
748 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("21h1_release")) == 0)
749 {
750 pszVersion = "2103"; // ??
751 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
752 }
753 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("21h2_release")) == 0)
754 {
755 pszVersion = "2109"; // ??
756 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win10);
757 }
758 else if (RTStrNICmp(pBuf->sz, RT_STR_TUPLE("co_release")) == 0)
759 {
760 pszVersion = "21H2"; // ??
761 *penmOsType = VBOXOSTYPE_Win11_x64;
762 }
763 else
764 LogRel(("Unattended: sources/idwbinfo.txt: Unknown: BuildBranch=%s\n", pBuf->sz));
765 }
766 RTIniFileRelease(hIniFile);
767 }
768 }
769 bool fClarifyProd = false;
770 if (RT_FAILURE(vrc))
771 {
772 /*
773 * Check a INF file with a DriverVer that is updated with each service pack.
774 * DriverVer=10/01/2002,5.2.3790.3959
775 */
776 vrc = RTVfsFileOpen(hVfsIso, "AMD64/HIVESYS.INF", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
777 if (RT_SUCCESS(vrc))
778 *penmOsType = VBOXOSTYPE_WinNT_x64;
779 else
780 {
781 vrc = RTVfsFileOpen(hVfsIso, "I386/HIVESYS.INF", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
782 if (RT_SUCCESS(vrc))
783 *penmOsType = VBOXOSTYPE_WinNT;
784 }
785 if (RT_SUCCESS(vrc))
786 {
787 RTINIFILE hIniFile;
788 vrc = RTIniFileCreateFromVfsFile(&hIniFile, hVfsFile, RTINIFILE_F_READONLY);
789 RTVfsFileRelease(hVfsFile);
790 if (RT_SUCCESS(vrc))
791 {
792 vrc = RTIniFileQueryValue(hIniFile, "Version", "DriverVer", pBuf->sz, sizeof(*pBuf), NULL);
793 if (RT_SUCCESS(vrc))
794 {
795 LogRelFlow(("Unattended: HIVESYS.INF: DriverVer=%s\n", pBuf->sz));
796 const char *psz = strchr(pBuf->sz, ',');
797 psz = psz ? psz + 1 : pBuf->sz;
798 if (RTStrVersionCompare(psz, "6.0.0") >= 0)
799 LogRel(("Unattended: HIVESYS.INF: unknown: DriverVer=%s\n", psz));
800 else if (RTStrVersionCompare(psz, "5.2.0") >= 0) /* W2K3, XP64 */
801 {
802 fClarifyProd = true;
803 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win2k3);
804 if (RTStrVersionCompare(psz, "5.2.3790.3959") >= 0)
805 pszVersion = "sp2";
806 else if (RTStrVersionCompare(psz, "5.2.3790.1830") >= 0)
807 pszVersion = "sp1";
808 }
809 else if (RTStrVersionCompare(psz, "5.1.0") >= 0) /* XP */
810 {
811 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_WinXP);
812 if (RTStrVersionCompare(psz, "5.1.2600.5512") >= 0)
813 pszVersion = "sp3";
814 else if (RTStrVersionCompare(psz, "5.1.2600.2180") >= 0)
815 pszVersion = "sp2";
816 else if (RTStrVersionCompare(psz, "5.1.2600.1105") >= 0)
817 pszVersion = "sp1";
818 }
819 else if (RTStrVersionCompare(psz, "5.0.0") >= 0)
820 {
821 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win2k);
822 if (RTStrVersionCompare(psz, "5.0.2195.6717") >= 0)
823 pszVersion = "sp4";
824 else if (RTStrVersionCompare(psz, "5.0.2195.5438") >= 0)
825 pszVersion = "sp3";
826 else if (RTStrVersionCompare(psz, "5.0.2195.1620") >= 0)
827 pszVersion = "sp1";
828 }
829 else
830 LogRel(("Unattended: HIVESYS.INF: unknown: DriverVer=%s\n", psz));
831 }
832 RTIniFileRelease(hIniFile);
833 }
834 }
835 }
836 if (RT_FAILURE(vrc) || fClarifyProd)
837 {
838 /*
839 * NT 4 and older does not have DriverVer entries, we consult the PRODSPEC.INI, which
840 * works for NT4 & W2K. It does usually not reflect the service pack.
841 */
842 vrc = RTVfsFileOpen(hVfsIso, "AMD64/PRODSPEC.INI", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
843 if (RT_SUCCESS(vrc))
844 *penmOsType = VBOXOSTYPE_WinNT_x64;
845 else
846 {
847 vrc = RTVfsFileOpen(hVfsIso, "I386/PRODSPEC.INI", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
848 if (RT_SUCCESS(vrc))
849 *penmOsType = VBOXOSTYPE_WinNT;
850 }
851 if (RT_SUCCESS(vrc))
852 {
853
854 RTINIFILE hIniFile;
855 vrc = RTIniFileCreateFromVfsFile(&hIniFile, hVfsFile, RTINIFILE_F_READONLY);
856 RTVfsFileRelease(hVfsFile);
857 if (RT_SUCCESS(vrc))
858 {
859 vrc = RTIniFileQueryValue(hIniFile, "Product Specification", "Version", pBuf->sz, sizeof(*pBuf), NULL);
860 if (RT_SUCCESS(vrc))
861 {
862 LogRelFlow(("Unattended: PRODSPEC.INI: Version=%s\n", pBuf->sz));
863 if (RTStrVersionCompare(pBuf->sz, "5.1") >= 0) /* Shipped with XP + W2K3, but version stuck at 5.0. */
864 LogRel(("Unattended: PRODSPEC.INI: unknown: DriverVer=%s\n", pBuf->sz));
865 else if (RTStrVersionCompare(pBuf->sz, "5.0") >= 0) /* 2000 */
866 {
867 vrc = RTIniFileQueryValue(hIniFile, "Product Specification", "Product", pBuf->sz, sizeof(*pBuf), NULL);
868 if (RT_SUCCESS(vrc) && RTStrNICmp(pBuf->sz, RT_STR_TUPLE("Windows XP")) == 0)
869 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_WinXP);
870 else if (RT_SUCCESS(vrc) && RTStrNICmp(pBuf->sz, RT_STR_TUPLE("Windows Server 2003")) == 0)
871 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win2k3);
872 else
873 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Win2k);
874
875 if (RT_SUCCESS(vrc) && (strstr(pBuf->sz, "Server") || strstr(pBuf->sz, "server")))
876 pszProduct = "Server";
877 }
878 else if (RTStrVersionCompare(pBuf->sz, "4.0") >= 0) /* NT4 */
879 *penmOsType = VBOXOSTYPE_WinNT4;
880 else
881 LogRel(("Unattended: PRODSPEC.INI: unknown: DriverVer=%s\n", pBuf->sz));
882
883 vrc = RTIniFileQueryValue(hIniFile, "Product Specification", "ProductType", pBuf->sz, sizeof(*pBuf), NULL);
884 if (RT_SUCCESS(vrc))
885 pszProduct = strcmp(pBuf->sz, "0") == 0 ? "Workstation" : /* simplification: */ "Server";
886 }
887 RTIniFileRelease(hIniFile);
888 }
889 }
890 if (fClarifyProd)
891 vrc = VINF_SUCCESS;
892 }
893 if (RT_FAILURE(vrc))
894 {
895 /*
896 * NT 3.x we look at the LoadIdentifier (boot manager) string in TXTSETUP.SIF/TXT.
897 */
898 vrc = RTVfsFileOpen(hVfsIso, "I386/TXTSETUP.SIF", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
899 if (RT_FAILURE(vrc))
900 vrc = RTVfsFileOpen(hVfsIso, "I386/TXTSETUP.INF", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
901 if (RT_SUCCESS(vrc))
902 {
903 *penmOsType = VBOXOSTYPE_WinNT;
904
905 RTINIFILE hIniFile;
906 vrc = RTIniFileCreateFromVfsFile(&hIniFile, hVfsFile, RTINIFILE_F_READONLY);
907 RTVfsFileRelease(hVfsFile);
908 if (RT_SUCCESS(vrc))
909 {
910 vrc = RTIniFileQueryValue(hIniFile, "SetupData", "ProductType", pBuf->sz, sizeof(*pBuf), NULL);
911 if (RT_SUCCESS(vrc))
912 pszProduct = strcmp(pBuf->sz, "0") == 0 ? "Workstation" : /* simplification: */ "Server";
913
914 vrc = RTIniFileQueryValue(hIniFile, "SetupData", "LoadIdentifier", pBuf->sz, sizeof(*pBuf), NULL);
915 if (RT_SUCCESS(vrc))
916 {
917 LogRelFlow(("Unattended: TXTSETUP.SIF: LoadIdentifier=%s\n", pBuf->sz));
918 char *psz = pBuf->sz;
919 while (!RT_C_IS_DIGIT(*psz) && *psz)
920 psz++;
921 char *psz2 = psz;
922 while (RT_C_IS_DIGIT(*psz2) || *psz2 == '.')
923 psz2++;
924 *psz2 = '\0';
925 if (RTStrVersionCompare(psz, "6.0") >= 0)
926 LogRel(("Unattended: TXTSETUP.SIF: unknown: LoadIdentifier=%s\n", pBuf->sz));
927 else if (RTStrVersionCompare(psz, "4.0") >= 0)
928 *penmOsType = VBOXOSTYPE_WinNT4;
929 else if (RTStrVersionCompare(psz, "3.1") >= 0)
930 {
931 *penmOsType = VBOXOSTYPE_WinNT3x;
932 pszVersion = psz;
933 }
934 else
935 LogRel(("Unattended: TXTSETUP.SIF: unknown: LoadIdentifier=%s\n", pBuf->sz));
936 }
937 RTIniFileRelease(hIniFile);
938 }
939 }
940 }
941
942 if (pszVersion)
943 try { mStrDetectedOSVersion = pszVersion; }
944 catch (std::bad_alloc &) { return E_OUTOFMEMORY; }
945 if (pszProduct)
946 try { mStrDetectedOSFlavor = pszProduct; }
947 catch (std::bad_alloc &) { return E_OUTOFMEMORY; }
948
949 /*
950 * Look for sources/lang.ini and try parse it to get the languages out of it.
951 */
952 /** @todo We could also check sources/??-* and boot/??-* if lang.ini is not
953 * found or unhelpful. */
954 vrc = RTVfsFileOpen(hVfsIso, "sources/lang.ini", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
955 if (RT_SUCCESS(vrc))
956 {
957 RTINIFILE hIniFile;
958 vrc = RTIniFileCreateFromVfsFile(&hIniFile, hVfsFile, RTINIFILE_F_READONLY);
959 RTVfsFileRelease(hVfsFile);
960 if (RT_SUCCESS(vrc))
961 {
962 mDetectedOSLanguages.clear();
963
964 uint32_t idxPair;
965 for (idxPair = 0; idxPair < 256; idxPair++)
966 {
967 size_t cbHalf = sizeof(*pBuf) / 2;
968 char *pszKey = pBuf->sz;
969 char *pszValue = &pBuf->sz[cbHalf];
970 vrc = RTIniFileQueryPair(hIniFile, "Available UI Languages", idxPair,
971 pszKey, cbHalf, NULL, pszValue, cbHalf, NULL);
972 if (RT_SUCCESS(vrc))
973 {
974 try
975 {
976 mDetectedOSLanguages.append(pszKey);
977 }
978 catch (std::bad_alloc &)
979 {
980 RTIniFileRelease(hIniFile);
981 return E_OUTOFMEMORY;
982 }
983 }
984 else if (vrc == VERR_NOT_FOUND)
985 break;
986 else
987 Assert(vrc == VERR_BUFFER_OVERFLOW);
988 }
989 if (idxPair == 0)
990 LogRel(("Unattended: Warning! Empty 'Available UI Languages' section in sources/lang.ini\n"));
991 RTIniFileRelease(hIniFile);
992 }
993 }
994
995 return S_FALSE;
996}
997
998/**
999 * Detects linux architecture.
1000 *
1001 * @returns true if detected, false if not.
1002 * @param pszArch The architecture string.
1003 * @param penmOsType Where to return the arch and type on success.
1004 * @param enmBaseOsType The base (x86) OS type to return.
1005 */
1006static bool detectLinuxArch(const char *pszArch, VBOXOSTYPE *penmOsType, VBOXOSTYPE enmBaseOsType)
1007{
1008 if ( RTStrNICmp(pszArch, RT_STR_TUPLE("amd64")) == 0
1009 || RTStrNICmp(pszArch, RT_STR_TUPLE("x86_64")) == 0
1010 || RTStrNICmp(pszArch, RT_STR_TUPLE("x86-64")) == 0 /* just in case */
1011 || RTStrNICmp(pszArch, RT_STR_TUPLE("x64")) == 0 /* ditto */ )
1012 {
1013 *penmOsType = (VBOXOSTYPE)(enmBaseOsType | VBOXOSTYPE_x64);
1014 return true;
1015 }
1016
1017 if ( RTStrNICmp(pszArch, RT_STR_TUPLE("x86")) == 0
1018 || RTStrNICmp(pszArch, RT_STR_TUPLE("i386")) == 0
1019 || RTStrNICmp(pszArch, RT_STR_TUPLE("i486")) == 0
1020 || RTStrNICmp(pszArch, RT_STR_TUPLE("i586")) == 0
1021 || RTStrNICmp(pszArch, RT_STR_TUPLE("i686")) == 0
1022 || RTStrNICmp(pszArch, RT_STR_TUPLE("i786")) == 0
1023 || RTStrNICmp(pszArch, RT_STR_TUPLE("i886")) == 0
1024 || RTStrNICmp(pszArch, RT_STR_TUPLE("i986")) == 0)
1025 {
1026 *penmOsType = enmBaseOsType;
1027 return true;
1028 }
1029
1030 /** @todo check for 'noarch' since source CDs have been seen to use that. */
1031 return false;
1032}
1033
1034/**
1035 * Detects linux architecture by searching for the architecture substring in @p pszArch.
1036 *
1037 * @returns true if detected, false if not.
1038 * @param pszArch The architecture string.
1039 * @param penmOsType Where to return the arch and type on success.
1040 * @param enmBaseOsType The base (x86) OS type to return.
1041 */
1042static bool detectLinuxArchII(const char *pszArch, VBOXOSTYPE *penmOsType, VBOXOSTYPE enmBaseOsType)
1043{
1044 if ( RTStrIStr(pszArch, "amd64") != NULL
1045 || RTStrIStr(pszArch, "x86_64") != NULL
1046 || RTStrIStr(pszArch, "x86-64") != NULL /* just in case */
1047 || RTStrIStr(pszArch, "x64") != NULL /* ditto */ )
1048 {
1049 *penmOsType = (VBOXOSTYPE)(enmBaseOsType | VBOXOSTYPE_x64);
1050 return true;
1051 }
1052
1053 if ( RTStrIStr(pszArch, "x86") != NULL
1054 || RTStrIStr(pszArch, "i386") != NULL
1055 || RTStrIStr(pszArch, "i486") != NULL
1056 || RTStrIStr(pszArch, "i586") != NULL
1057 || RTStrIStr(pszArch, "i686") != NULL
1058 || RTStrIStr(pszArch, "i786") != NULL
1059 || RTStrIStr(pszArch, "i886") != NULL
1060 || RTStrIStr(pszArch, "i986") != NULL)
1061 {
1062 *penmOsType = enmBaseOsType;
1063 return true;
1064 }
1065 return false;
1066}
1067
1068static bool detectLinuxDistroName(const char *pszOsAndVersion, VBOXOSTYPE *penmOsType, const char **ppszNext)
1069{
1070 bool fRet = true;
1071
1072 if ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("Red")) == 0
1073 && !RT_C_IS_ALNUM(pszOsAndVersion[3]))
1074
1075 {
1076 pszOsAndVersion = RTStrStripL(pszOsAndVersion + 3);
1077 if ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("Hat")) == 0
1078 && !RT_C_IS_ALNUM(pszOsAndVersion[3]))
1079 {
1080 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_RedHat);
1081 pszOsAndVersion = RTStrStripL(pszOsAndVersion + 3);
1082 }
1083 else
1084 fRet = false;
1085 }
1086 else if ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("Oracle")) == 0
1087 && !RT_C_IS_ALNUM(pszOsAndVersion[6]))
1088 {
1089 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Oracle);
1090 pszOsAndVersion = RTStrStripL(pszOsAndVersion + 6);
1091 }
1092 else if ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("CentOS")) == 0
1093 && !RT_C_IS_ALNUM(pszOsAndVersion[6]))
1094 {
1095 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_RedHat);
1096 pszOsAndVersion = RTStrStripL(pszOsAndVersion + 6);
1097 }
1098 else if ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("Fedora")) == 0
1099 && !RT_C_IS_ALNUM(pszOsAndVersion[6]))
1100 {
1101 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_FedoraCore);
1102 pszOsAndVersion = RTStrStripL(pszOsAndVersion + 6);
1103 }
1104 else if ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("Ubuntu")) == 0
1105 && !RT_C_IS_ALNUM(pszOsAndVersion[6]))
1106 {
1107 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Ubuntu);
1108 pszOsAndVersion = RTStrStripL(pszOsAndVersion + 6);
1109 }
1110 else if ( ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("Xubuntu")) == 0
1111 || RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("Kubuntu")) == 0)
1112 && !RT_C_IS_ALNUM(pszOsAndVersion[7]))
1113 {
1114 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Ubuntu);
1115 pszOsAndVersion = RTStrStripL(pszOsAndVersion + 7);
1116 }
1117 else if ( RTStrNICmp(pszOsAndVersion, RT_STR_TUPLE("Debian")) == 0
1118 && !RT_C_IS_ALNUM(pszOsAndVersion[6]))
1119 {
1120 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_Debian);
1121 pszOsAndVersion = RTStrStripL(pszOsAndVersion + 6);
1122 }
1123 else
1124 fRet = false;
1125
1126 /*
1127 * Skip forward till we get a number.
1128 */
1129 if (ppszNext)
1130 {
1131 *ppszNext = pszOsAndVersion;
1132 char ch;
1133 for (const char *pszVersion = pszOsAndVersion; (ch = *pszVersion) != '\0'; pszVersion++)
1134 if (RT_C_IS_DIGIT(ch))
1135 {
1136 *ppszNext = pszVersion;
1137 break;
1138 }
1139 }
1140 return fRet;
1141}
1142
1143
1144/**
1145 * Detect Linux distro ISOs.
1146 *
1147 * @returns COM status code.
1148 * @retval S_OK if detected
1149 * @retval S_FALSE if not fully detected.
1150 *
1151 * @param hVfsIso The ISO file system.
1152 * @param pBuf Read buffer.
1153 * @param penmOsType Where to return the OS type. This is initialized to
1154 * VBOXOSTYPE_Unknown.
1155 */
1156HRESULT Unattended::i_innerDetectIsoOSLinux(RTVFS hVfsIso, DETECTBUFFER *pBuf, VBOXOSTYPE *penmOsType)
1157{
1158 /*
1159 * Redhat and derivatives may have a .treeinfo (ini-file style) with useful info
1160 * or at least a barebone .discinfo file.
1161 */
1162
1163 /*
1164 * Start with .treeinfo: https://release-engineering.github.io/productmd/treeinfo-1.0.html
1165 */
1166 RTVFSFILE hVfsFile;
1167 int vrc = RTVfsFileOpen(hVfsIso, ".treeinfo", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
1168 if (RT_SUCCESS(vrc))
1169 {
1170 RTINIFILE hIniFile;
1171 vrc = RTIniFileCreateFromVfsFile(&hIniFile, hVfsFile, RTINIFILE_F_READONLY);
1172 RTVfsFileRelease(hVfsFile);
1173 if (RT_SUCCESS(vrc))
1174 {
1175 /* Try figure the architecture first (like with windows). */
1176 vrc = RTIniFileQueryValue(hIniFile, "tree", "arch", pBuf->sz, sizeof(*pBuf), NULL);
1177 if (RT_FAILURE(vrc) || !pBuf->sz[0])
1178 vrc = RTIniFileQueryValue(hIniFile, "general", "arch", pBuf->sz, sizeof(*pBuf), NULL);
1179 if (RT_FAILURE(vrc))
1180 LogRel(("Unattended: .treeinfo: No 'arch' property.\n"));
1181 else
1182 {
1183 LogRelFlow(("Unattended: .treeinfo: arch=%s\n", pBuf->sz));
1184 if (detectLinuxArch(pBuf->sz, penmOsType, VBOXOSTYPE_RedHat))
1185 {
1186 /* Try figure the release name, it doesn't have to be redhat. */
1187 vrc = RTIniFileQueryValue(hIniFile, "release", "name", pBuf->sz, sizeof(*pBuf), NULL);
1188 if (RT_FAILURE(vrc) || !pBuf->sz[0])
1189 vrc = RTIniFileQueryValue(hIniFile, "product", "name", pBuf->sz, sizeof(*pBuf), NULL);
1190 if (RT_FAILURE(vrc) || !pBuf->sz[0])
1191 vrc = RTIniFileQueryValue(hIniFile, "general", "family", pBuf->sz, sizeof(*pBuf), NULL);
1192 if (RT_SUCCESS(vrc))
1193 {
1194 LogRelFlow(("Unattended: .treeinfo: name/family=%s\n", pBuf->sz));
1195 if (!detectLinuxDistroName(pBuf->sz, penmOsType, NULL))
1196 {
1197 LogRel(("Unattended: .treeinfo: Unknown: name/family='%s', assuming Red Hat\n", pBuf->sz));
1198 *penmOsType = (VBOXOSTYPE)((*penmOsType & VBOXOSTYPE_x64) | VBOXOSTYPE_RedHat);
1199 }
1200 }
1201
1202 /* Try figure the version. */
1203 vrc = RTIniFileQueryValue(hIniFile, "release", "version", pBuf->sz, sizeof(*pBuf), NULL);
1204 if (RT_FAILURE(vrc) || !pBuf->sz[0])
1205 vrc = RTIniFileQueryValue(hIniFile, "product", "version", pBuf->sz, sizeof(*pBuf), NULL);
1206 if (RT_FAILURE(vrc) || !pBuf->sz[0])
1207 vrc = RTIniFileQueryValue(hIniFile, "general", "version", pBuf->sz, sizeof(*pBuf), NULL);
1208 if (RT_SUCCESS(vrc))
1209 {
1210 LogRelFlow(("Unattended: .treeinfo: version=%s\n", pBuf->sz));
1211 try { mStrDetectedOSVersion = RTStrStrip(pBuf->sz); }
1212 catch (std::bad_alloc &) { return E_OUTOFMEMORY; }
1213 }
1214 }
1215 else
1216 LogRel(("Unattended: .treeinfo: Unknown: arch='%s'\n", pBuf->sz));
1217 }
1218
1219 RTIniFileRelease(hIniFile);
1220 }
1221
1222 if (*penmOsType != VBOXOSTYPE_Unknown)
1223 return S_FALSE;
1224 }
1225
1226 /*
1227 * Try .discinfo next: https://release-engineering.github.io/productmd/discinfo-1.0.html
1228 * We will probably need additional info here...
1229 */
1230 vrc = RTVfsFileOpen(hVfsIso, ".discinfo", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
1231 if (RT_SUCCESS(vrc))
1232 {
1233 size_t cchIgn;
1234 vrc = RTVfsFileRead(hVfsFile, pBuf->sz, sizeof(*pBuf) - 1, &cchIgn);
1235 pBuf->sz[RT_SUCCESS(vrc) ? cchIgn : 0] = '\0';
1236 RTVfsFileRelease(hVfsFile);
1237
1238 /* Parse and strip the first 5 lines. */
1239 const char *apszLines[5];
1240 char *psz = pBuf->sz;
1241 for (unsigned i = 0; i < RT_ELEMENTS(apszLines); i++)
1242 {
1243 apszLines[i] = psz;
1244 if (*psz)
1245 {
1246 char *pszEol = (char *)strchr(psz, '\n');
1247 if (!pszEol)
1248 psz = strchr(psz, '\0');
1249 else
1250 {
1251 *pszEol = '\0';
1252 apszLines[i] = RTStrStrip(psz);
1253 psz = pszEol + 1;
1254 }
1255 }
1256 }
1257
1258 /* Do we recognize the architecture? */
1259 LogRelFlow(("Unattended: .discinfo: arch=%s\n", apszLines[2]));
1260 if (detectLinuxArch(apszLines[2], penmOsType, VBOXOSTYPE_RedHat))
1261 {
1262 /* Do we recognize the release string? */
1263 LogRelFlow(("Unattended: .discinfo: product+version=%s\n", apszLines[1]));
1264 const char *pszVersion = NULL;
1265 if (!detectLinuxDistroName(apszLines[1], penmOsType, &pszVersion))
1266 LogRel(("Unattended: .discinfo: Unknown: release='%s'\n", apszLines[1]));
1267
1268 if (*pszVersion)
1269 {
1270 LogRelFlow(("Unattended: .discinfo: version=%s\n", pszVersion));
1271 try { mStrDetectedOSVersion = RTStrStripL(pszVersion); }
1272 catch (std::bad_alloc &) { return E_OUTOFMEMORY; }
1273
1274 /* CentOS likes to call their release 'Final' without mentioning the actual version
1275 number (e.g. CentOS-4.7-x86_64-binDVD.iso), so we need to go look elsewhere.
1276 This is only important for centos 4.x and 3.x releases. */
1277 if (RTStrNICmp(pszVersion, RT_STR_TUPLE("Final")) == 0)
1278 {
1279 static const char * const s_apszDirs[] = { "CentOS/RPMS/", "RedHat/RPMS", "Server", "Workstation" };
1280 for (unsigned iDir = 0; iDir < RT_ELEMENTS(s_apszDirs); iDir++)
1281 {
1282 RTVFSDIR hVfsDir;
1283 vrc = RTVfsDirOpen(hVfsIso, s_apszDirs[iDir], 0, &hVfsDir);
1284 if (RT_FAILURE(vrc))
1285 continue;
1286 char szRpmDb[128];
1287 char szReleaseRpm[128];
1288 szRpmDb[0] = '\0';
1289 szReleaseRpm[0] = '\0';
1290 for (;;)
1291 {
1292 RTDIRENTRYEX DirEntry;
1293 size_t cbDirEntry = sizeof(DirEntry);
1294 vrc = RTVfsDirReadEx(hVfsDir, &DirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING);
1295 if (RT_FAILURE(vrc))
1296 break;
1297
1298 /* redhat-release-4WS-2.4.i386.rpm
1299 centos-release-4-7.x86_64.rpm, centos-release-4-4.3.i386.rpm
1300 centos-release-5-3.el5.centos.1.x86_64.rpm */
1301 if ( (psz = strstr(DirEntry.szName, "-release-")) != NULL
1302 || (psz = strstr(DirEntry.szName, "-RELEASE-")) != NULL)
1303 {
1304 psz += 9;
1305 if (RT_C_IS_DIGIT(*psz))
1306 RTStrCopy(szReleaseRpm, sizeof(szReleaseRpm), psz);
1307 }
1308 /* rpmdb-redhat-4WS-2.4.i386.rpm,
1309 rpmdb-CentOS-4.5-0.20070506.i386.rpm,
1310 rpmdb-redhat-3.9-0.20070703.i386.rpm. */
1311 else if ( ( RTStrStartsWith(DirEntry.szName, "rpmdb-")
1312 || RTStrStartsWith(DirEntry.szName, "RPMDB-"))
1313 && RT_C_IS_DIGIT(DirEntry.szName[6]) )
1314 RTStrCopy(szRpmDb, sizeof(szRpmDb), &DirEntry.szName[6]);
1315 }
1316 RTVfsDirRelease(hVfsDir);
1317
1318 /* Did we find anything relvant? */
1319 psz = szRpmDb;
1320 if (!RT_C_IS_DIGIT(*psz))
1321 psz = szReleaseRpm;
1322 if (RT_C_IS_DIGIT(*psz))
1323 {
1324 /* Convert '-' to '.' and strip stuff which doesn't look like a version string. */
1325 char *pszCur = psz + 1;
1326 for (char ch = *pszCur; ch != '\0'; ch = *++pszCur)
1327 if (ch == '-')
1328 *pszCur = '.';
1329 else if (ch != '.' && !RT_C_IS_DIGIT(ch))
1330 {
1331 *pszCur = '\0';
1332 break;
1333 }
1334 while (&pszCur[-1] != psz && pszCur[-1] == '.')
1335 *--pszCur = '\0';
1336
1337 /* Set it and stop looking. */
1338 try { mStrDetectedOSVersion = psz; }
1339 catch (std::bad_alloc &) { return E_OUTOFMEMORY; }
1340 break;
1341 }
1342 }
1343 }
1344 }
1345 }
1346 else
1347 LogRel(("Unattended: .discinfo: Unknown: arch='%s'\n", apszLines[2]));
1348
1349 if (*penmOsType != VBOXOSTYPE_Unknown)
1350 return S_FALSE;
1351 }
1352
1353 /*
1354 * Ubuntu has a README.diskdefins file on their ISO (already on 4.10 / warty warthog).
1355 * Example content:
1356 * #define DISKNAME Ubuntu 4.10 "Warty Warthog" - Preview amd64 Binary-1
1357 * #define TYPE binary
1358 * #define TYPEbinary 1
1359 * #define ARCH amd64
1360 * #define ARCHamd64 1
1361 * #define DISKNUM 1
1362 * #define DISKNUM1 1
1363 * #define TOTALNUM 1
1364 * #define TOTALNUM1 1
1365 */
1366 vrc = RTVfsFileOpen(hVfsIso, "README.diskdefines", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
1367 if (RT_SUCCESS(vrc))
1368 {
1369 size_t cchIgn;
1370 vrc = RTVfsFileRead(hVfsFile, pBuf->sz, sizeof(*pBuf) - 1, &cchIgn);
1371 pBuf->sz[RT_SUCCESS(vrc) ? cchIgn : 0] = '\0';
1372 RTVfsFileRelease(hVfsFile);
1373
1374 /* Find the DISKNAME and ARCH defines. */
1375 const char *pszDiskName = NULL;
1376 const char *pszArch = NULL;
1377 char *psz = pBuf->sz;
1378 for (unsigned i = 0; *psz != '\0'; i++)
1379 {
1380 while (RT_C_IS_BLANK(*psz))
1381 psz++;
1382
1383 /* Match #define: */
1384 static const char s_szDefine[] = "#define";
1385 if ( strncmp(psz, s_szDefine, sizeof(s_szDefine) - 1) == 0
1386 && RT_C_IS_BLANK(psz[sizeof(s_szDefine) - 1]))
1387 {
1388 psz = &psz[sizeof(s_szDefine) - 1];
1389 while (RT_C_IS_BLANK(*psz))
1390 psz++;
1391
1392 /* Match the identifier: */
1393 char *pszIdentifier = psz;
1394 if (RT_C_IS_ALPHA(*psz) || *psz == '_')
1395 {
1396 do
1397 psz++;
1398 while (RT_C_IS_ALNUM(*psz) || *psz == '_');
1399 size_t cchIdentifier = (size_t)(psz - pszIdentifier);
1400
1401 /* Skip to the value. */
1402 while (RT_C_IS_BLANK(*psz))
1403 psz++;
1404 char *pszValue = psz;
1405
1406 /* Skip to EOL and strip the value. */
1407 char *pszEol = psz = strchr(psz, '\n');
1408 if (psz)
1409 *psz++ = '\0';
1410 else
1411 pszEol = strchr(pszValue, '\0');
1412 while (pszEol > pszValue && RT_C_IS_SPACE(pszEol[-1]))
1413 *--pszEol = '\0';
1414
1415 LogRelFlow(("Unattended: README.diskdefines: %.*s=%s\n", cchIdentifier, pszIdentifier, pszValue));
1416
1417 /* Do identifier matching: */
1418 if (cchIdentifier == sizeof("DISKNAME") - 1 && strncmp(pszIdentifier, RT_STR_TUPLE("DISKNAME")) == 0)
1419 pszDiskName = pszValue;
1420 else if (cchIdentifier == sizeof("ARCH") - 1 && strncmp(pszIdentifier, RT_STR_TUPLE("ARCH")) == 0)
1421 pszArch = pszValue;
1422 else
1423 continue;
1424 if (pszDiskName == NULL || pszArch == NULL)
1425 continue;
1426 break;
1427 }
1428 }
1429
1430 /* Next line: */
1431 psz = strchr(psz, '\n');
1432 if (!psz)
1433 break;
1434 psz++;
1435 }
1436
1437 /* Did we find both of them? */
1438 if (pszDiskName && pszArch)
1439 {
1440 if (detectLinuxArch(pszArch, penmOsType, VBOXOSTYPE_Ubuntu))
1441 {
1442 const char *pszVersion = NULL;
1443 if (detectLinuxDistroName(pszDiskName, penmOsType, &pszVersion))
1444 {
1445 LogRelFlow(("Unattended: README.diskdefines: version=%s\n", pszVersion));
1446 try { mStrDetectedOSVersion = RTStrStripL(pszVersion); }
1447 catch (std::bad_alloc &) { return E_OUTOFMEMORY; }
1448 }
1449 else
1450 LogRel(("Unattended: README.diskdefines: Unknown: diskname='%s'\n", pszDiskName));
1451 }
1452 else
1453 LogRel(("Unattended: README.diskdefines: Unknown: arch='%s'\n", pszArch));
1454 }
1455 else
1456 LogRel(("Unattended: README.diskdefines: Did not find both DISKNAME and ARCH. :-/\n"));
1457
1458 if (*penmOsType != VBOXOSTYPE_Unknown)
1459 return S_FALSE;
1460 }
1461
1462 /*
1463 * All of the debian based distro versions I checked have a single line ./disk/info file.
1464 * Only info I could find related to .disk folder is: https://lists.debian.org/debian-cd/2004/01/msg00069.html
1465 * Some example content from several install ISOs is as follows:
1466 * Ubuntu 4.10 "Warty Warthog" - Preview amd64 Binary-1 (20041020)
1467 * Linux Mint 20.3 "Una" - Release amd64 20220104
1468 * Debian GNU/Linux 11.2.0 "Bullseye" - Official amd64 NETINST 20211218-11:12
1469 * Debian GNU/Linux 9.13.0 "Stretch" - Official amd64 DVD Binary-1 20200718-11:07
1470 * Xubuntu 20.04.2.0 LTS "Focal Fossa" - Release amd64 (20210209.1)
1471 * Ubuntu 17.10 "Artful Aardvark" - Release amd64 (20180105.1)
1472 * Ubuntu 16.04.6 LTS "Xenial Xerus" - Release i386 (20190227.1)
1473 * Debian GNU/Linux 8.11.1 "Jessie" - Official amd64 CD Binary-1 20190211-02:10
1474 * Kali GNU/Linux 2021.3a "Kali-last-snapshot" - Official amd64 BD Binary-1 with firmware 20211015-16:55
1475 */
1476 vrc = RTVfsFileOpen(hVfsIso, ".disk/info", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
1477 if (RT_SUCCESS(vrc))
1478 {
1479 size_t cchIgn;
1480 vrc = RTVfsFileRead(hVfsFile, pBuf->sz, sizeof(*pBuf) - 1, &cchIgn);
1481 pBuf->sz[RT_SUCCESS(vrc) ? cchIgn : 0] = '\0';
1482
1483 pBuf->sz[sizeof(*pBuf) - 1] = '\0';
1484 RTVfsFileRelease(hVfsFile);
1485
1486 char *psz = pBuf->sz;
1487 char *pszDiskName = psz;
1488 char *pszArch = NULL;
1489
1490 /* Only care about the first line of the file even if it is multi line and assume disk name ended with ' - '.*/
1491 psz = RTStrStr(pBuf->sz, " - ");
1492 if (psz && memchr(pBuf->sz, '\n', (size_t)(psz - pBuf->sz)) == NULL)
1493 {
1494 *psz = '\0';
1495 psz += 3;
1496 if (*psz)
1497 pszArch = psz;
1498 }
1499
1500 if (pszDiskName && pszArch)
1501 {
1502 if (!detectLinuxArchII(pszArch, penmOsType, VBOXOSTYPE_Ubuntu))
1503 LogRel(("Unattended: README.diskdefines: Unknown: arch='%s'\n", pszArch));
1504
1505 const char *pszVersion = NULL;
1506 if (detectLinuxDistroName(pszDiskName, penmOsType, &pszVersion))
1507 {
1508 LogRelFlow(("Unattended: README.diskdefines: version=%s\n", pszVersion));
1509 try { mStrDetectedOSVersion = RTStrStripL(pszVersion); }
1510 catch (std::bad_alloc &) { return E_OUTOFMEMORY; }
1511 }
1512 else
1513 LogRel(("Unattended: README.diskdefines: Unknown: diskname='%s'\n", pszDiskName));
1514 }
1515 else
1516 LogRel(("Unattended: README.diskdefines: Did not find both DISKNAME and ARCH. :-/\n"));
1517
1518 if (*penmOsType != VBOXOSTYPE_Unknown)
1519 return S_FALSE;
1520 }
1521
1522 return S_FALSE;
1523}
1524
1525
1526/**
1527 * Detect OS/2 installation ISOs.
1528 *
1529 * Mainly aiming at ACP2/MCP2 as that's what we currently use in our testing.
1530 *
1531 * @returns COM status code.
1532 * @retval S_OK if detected
1533 * @retval S_FALSE if not fully detected.
1534 *
1535 * @param hVfsIso The ISO file system.
1536 * @param pBuf Read buffer.
1537 * @param penmOsType Where to return the OS type. This is initialized to
1538 * VBOXOSTYPE_Unknown.
1539 */
1540HRESULT Unattended::i_innerDetectIsoOSOs2(RTVFS hVfsIso, DETECTBUFFER *pBuf, VBOXOSTYPE *penmOsType)
1541{
1542 /*
1543 * The OS2SE20.SRC contains the location of the tree with the diskette
1544 * images, typically "\OS2IMAGE".
1545 */
1546 RTVFSFILE hVfsFile;
1547 int vrc = RTVfsFileOpen(hVfsIso, "OS2SE20.SRC", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
1548 if (RT_SUCCESS(vrc))
1549 {
1550 size_t cbRead = 0;
1551 vrc = RTVfsFileRead(hVfsFile, pBuf->sz, sizeof(pBuf->sz) - 1, &cbRead);
1552 RTVfsFileRelease(hVfsFile);
1553 if (RT_SUCCESS(vrc))
1554 {
1555 pBuf->sz[cbRead] = '\0';
1556 RTStrStrip(pBuf->sz);
1557 vrc = RTStrValidateEncoding(pBuf->sz);
1558 if (RT_SUCCESS(vrc))
1559 LogRelFlow(("Unattended: OS2SE20.SRC=%s\n", pBuf->sz));
1560 else
1561 LogRel(("Unattended: OS2SE20.SRC invalid encoding: %Rrc, %.*Rhxs\n", vrc, cbRead, pBuf->sz));
1562 }
1563 else
1564 LogRel(("Unattended: Error reading OS2SE20.SRC: %\n", vrc));
1565 }
1566 /*
1567 * ArcaOS has dropped the file, assume it's \OS2IMAGE and see if it's there.
1568 */
1569 else if (vrc == VERR_FILE_NOT_FOUND)
1570 RTStrCopy(pBuf->sz, sizeof(pBuf->sz), "\\OS2IMAGE");
1571 else
1572 return S_FALSE;
1573
1574 /*
1575 * Check that the directory directory exists and has a DISK_0 under it
1576 * with an OS2LDR on it.
1577 */
1578 size_t const cchOs2Image = strlen(pBuf->sz);
1579 vrc = RTPathAppend(pBuf->sz, sizeof(pBuf->sz), "DISK_0/OS2LDR");
1580 RTFSOBJINFO ObjInfo = {0};
1581 vrc = RTVfsQueryPathInfo(hVfsIso, pBuf->sz, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1582 if (vrc == VERR_FILE_NOT_FOUND)
1583 {
1584 RTStrCat(pBuf->sz, sizeof(pBuf->sz), "."); /* eCS 2.0 image includes the dot from the 8.3 name. */
1585 vrc = RTVfsQueryPathInfo(hVfsIso, pBuf->sz, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1586 }
1587 if ( RT_FAILURE(vrc)
1588 || !RTFS_IS_FILE(ObjInfo.Attr.fMode))
1589 {
1590 LogRel(("Unattended: RTVfsQueryPathInfo(, '%s' (from OS2SE20.SRC),) -> %Rrc, fMode=%#x\n",
1591 pBuf->sz, vrc, ObjInfo.Attr.fMode));
1592 return S_FALSE;
1593 }
1594
1595 /*
1596 * So, it's some kind of OS/2 2.x or later ISO alright.
1597 */
1598 *penmOsType = VBOXOSTYPE_OS2;
1599 mStrDetectedOSHints.printf("OS2SE20.SRC=%.*s", cchOs2Image, pBuf->sz);
1600
1601 /*
1602 * ArcaOS ISOs seems to have a AOSBOOT dir on them.
1603 * This contains a ARCANOAE.FLG file with content we can use for the version:
1604 * ArcaOS 5.0.7 EN
1605 * Built 2021-12-07 18:34:34
1606 * We drop the "ArcaOS" bit, as it's covered by penmOsType. Then we pull up
1607 * the second line.
1608 *
1609 * Note! Yet to find a way to do unattended install of ArcaOS, as it comes
1610 * with no CD-boot floppy images, only simple .PF archive files for
1611 * unpacking onto the ram disk or whatever. Modifying these is
1612 * possible (ibsen's aPLib v0.36 compression with some simple custom
1613 * headers), but it would probably be a royal pain. Could perhaps
1614 * cook something from OS2IMAGE\DISK_0 thru 3...
1615 */
1616 vrc = RTVfsQueryPathInfo(hVfsIso, "AOSBOOT", &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1617 if ( RT_SUCCESS(vrc)
1618 && RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
1619 {
1620 *penmOsType = VBOXOSTYPE_ArcaOS;
1621
1622 /* Read the version file: */
1623 vrc = RTVfsFileOpen(hVfsIso, "SYS/ARCANOAE.FLG", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
1624 if (RT_SUCCESS(vrc))
1625 {
1626 size_t cbRead = 0;
1627 vrc = RTVfsFileRead(hVfsFile, pBuf->sz, sizeof(pBuf->sz) - 1, &cbRead);
1628 RTVfsFileRelease(hVfsFile);
1629 pBuf->sz[cbRead] = '\0';
1630 if (RT_SUCCESS(vrc))
1631 {
1632 /* Strip the OS name: */
1633 char *pszVersion = RTStrStrip(pBuf->sz);
1634 static char s_szArcaOS[] = "ArcaOS";
1635 if (RTStrStartsWith(pszVersion, s_szArcaOS))
1636 pszVersion = RTStrStripL(pszVersion + sizeof(s_szArcaOS) - 1);
1637
1638 /* Pull up the 2nd line if it, condensing the \r\n into a single space. */
1639 char *pszNewLine = strchr(pszVersion, '\n');
1640 if (pszNewLine && RTStrStartsWith(pszNewLine + 1, "Built 20"))
1641 {
1642 size_t offRemove = 0;
1643 while (RT_C_IS_SPACE(pszNewLine[-1 - (ssize_t)offRemove]))
1644 offRemove++;
1645 if (offRemove > 0)
1646 {
1647 pszNewLine -= offRemove;
1648 memmove(pszNewLine, pszNewLine + offRemove, strlen(pszNewLine + offRemove) - 1);
1649 }
1650 *pszNewLine = ' ';
1651 }
1652
1653 /* Drop any additional lines: */
1654 pszNewLine = strchr(pszVersion, '\n');
1655 if (pszNewLine)
1656 *pszNewLine = '\0';
1657 RTStrStripR(pszVersion);
1658
1659 /* Done (hope it makes some sense). */
1660 mStrDetectedOSVersion = pszVersion;
1661 }
1662 else
1663 LogRel(("Unattended: failed to read AOSBOOT/ARCANOAE.FLG: %Rrc\n", vrc));
1664 }
1665 else
1666 LogRel(("Unattended: failed to open AOSBOOT/ARCANOAE.FLG for reading: %Rrc\n", vrc));
1667 }
1668 /*
1669 * Similarly, eCS has an ECS directory and it typically contains a
1670 * ECS_INST.FLG file with the version info. Content differs a little:
1671 * eComStation 2.0 EN_US Thu May 13 10:27:54 pm 2010
1672 * Built on ECS60441318
1673 * Here we drop the "eComStation" bit and leave the 2nd line as it.
1674 *
1675 * Note! At least 2.0 has a DISKIMGS folder with what looks like boot
1676 * disks, so we could probably get something going here without
1677 * needing to write an OS2 boot sector...
1678 */
1679 else
1680 {
1681 vrc = RTVfsQueryPathInfo(hVfsIso, "ECS", &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1682 if ( RT_SUCCESS(vrc)
1683 && RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
1684 {
1685 *penmOsType = VBOXOSTYPE_ECS;
1686
1687 /* Read the version file: */
1688 vrc = RTVfsFileOpen(hVfsIso, "ECS/ECS_INST.FLG", RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
1689 if (RT_SUCCESS(vrc))
1690 {
1691 size_t cbRead = 0;
1692 vrc = RTVfsFileRead(hVfsFile, pBuf->sz, sizeof(pBuf->sz) - 1, &cbRead);
1693 RTVfsFileRelease(hVfsFile);
1694 pBuf->sz[cbRead] = '\0';
1695 if (RT_SUCCESS(vrc))
1696 {
1697 /* Strip the OS name: */
1698 char *pszVersion = RTStrStrip(pBuf->sz);
1699 static char s_szECS[] = "eComStation";
1700 if (RTStrStartsWith(pszVersion, s_szECS))
1701 pszVersion = RTStrStripL(pszVersion + sizeof(s_szECS) - 1);
1702
1703 /* Drop any additional lines: */
1704 char *pszNewLine = strchr(pszVersion, '\n');
1705 if (pszNewLine)
1706 *pszNewLine = '\0';
1707 RTStrStripR(pszVersion);
1708
1709 /* Done (hope it makes some sense). */
1710 mStrDetectedOSVersion = pszVersion;
1711 }
1712 else
1713 LogRel(("Unattended: failed to read ECS/ECS_INST.FLG: %Rrc\n", vrc));
1714 }
1715 else
1716 LogRel(("Unattended: failed to open ECS/ECS_INST.FLG for reading: %Rrc\n", vrc));
1717 }
1718 else
1719 {
1720 /*
1721 * Official IBM OS/2 builds doesn't have any .FLG file on them,
1722 * so need to pry the information out in some other way. Best way
1723 * is to read the SYSLEVEL.OS2 file, which is typically on disk #2,
1724 * though on earlier versions (warp3) it was disk #1.
1725 */
1726 vrc = RTPathJoin(pBuf->sz, sizeof(pBuf->sz), strchr(mStrDetectedOSHints.c_str(), '=') + 1,
1727 "/DISK_2/SYSLEVEL.OS2");
1728 if (RT_SUCCESS(vrc))
1729 {
1730 vrc = RTVfsFileOpen(hVfsIso, pBuf->sz, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
1731 if (vrc == VERR_FILE_NOT_FOUND)
1732 {
1733 RTPathJoin(pBuf->sz, sizeof(pBuf->sz), strchr(mStrDetectedOSHints.c_str(), '=') + 1, "/DISK_1/SYSLEVEL.OS2");
1734 vrc = RTVfsFileOpen(hVfsIso, pBuf->sz, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile);
1735 }
1736 if (RT_SUCCESS(vrc))
1737 {
1738 RT_ZERO(pBuf->ab);
1739 size_t cbRead = 0;
1740 vrc = RTVfsFileRead(hVfsFile, pBuf->ab, sizeof(pBuf->ab), &cbRead);
1741 RTVfsFileRelease(hVfsFile);
1742 if (RT_SUCCESS(vrc))
1743 {
1744 /* Check the header. */
1745 OS2SYSLEVELHDR const *pHdr = (OS2SYSLEVELHDR const *)&pBuf->ab[0];
1746 if ( pHdr->uMinusOne == UINT16_MAX
1747 && pHdr->uSyslevelFileVer == 1
1748 && memcmp(pHdr->achSignature, RT_STR_TUPLE("SYSLEVEL")) == 0
1749 && pHdr->offTable < cbRead
1750 && pHdr->offTable + sizeof(OS2SYSLEVELENTRY) <= cbRead)
1751 {
1752 OS2SYSLEVELENTRY *pEntry = (OS2SYSLEVELENTRY *)&pBuf->ab[pHdr->offTable];
1753 if ( RT_SUCCESS(RTStrValidateEncodingEx(pEntry->szName, sizeof(pEntry->szName),
1754 RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED))
1755 && RT_SUCCESS(RTStrValidateEncodingEx(pEntry->achCsdLevel, sizeof(pEntry->achCsdLevel), 0))
1756 && pEntry->bVersion != 0
1757 && ((pEntry->bVersion >> 4) & 0xf) < 10
1758 && (pEntry->bVersion & 0xf) < 10
1759 && pEntry->bModify < 10
1760 && pEntry->bRefresh < 10)
1761 {
1762 /* Flavor: */
1763 char *pszName = RTStrStrip(pEntry->szName);
1764 if (pszName)
1765 mStrDetectedOSFlavor = pszName;
1766
1767 /* Version: */
1768 if (pEntry->bRefresh != 0)
1769 mStrDetectedOSVersion.printf("%d.%d%d.%d", pEntry->bVersion >> 4, pEntry->bVersion & 0xf,
1770 pEntry->bModify, pEntry->bRefresh);
1771 else
1772 mStrDetectedOSVersion.printf("%d.%d%d", pEntry->bVersion >> 4, pEntry->bVersion & 0xf,
1773 pEntry->bModify);
1774 pEntry->achCsdLevel[sizeof(pEntry->achCsdLevel) - 1] = '\0';
1775 char *pszCsd = RTStrStrip(pEntry->achCsdLevel);
1776 if (*pszCsd != '\0')
1777 {
1778 mStrDetectedOSVersion.append(' ');
1779 mStrDetectedOSVersion.append(pszCsd);
1780 }
1781 if (RTStrVersionCompare(mStrDetectedOSVersion.c_str(), "4.50") >= 0)
1782 *penmOsType = VBOXOSTYPE_OS2Warp45;
1783 else if (RTStrVersionCompare(mStrDetectedOSVersion.c_str(), "4.00") >= 0)
1784 *penmOsType = VBOXOSTYPE_OS2Warp4;
1785 else if (RTStrVersionCompare(mStrDetectedOSVersion.c_str(), "3.00") >= 0)
1786 *penmOsType = VBOXOSTYPE_OS2Warp3;
1787 }
1788 else
1789 LogRel(("Unattended: bogus SYSLEVEL.OS2 file entry: %.128Rhxd\n", pEntry));
1790 }
1791 else
1792 LogRel(("Unattended: bogus SYSLEVEL.OS2 file header: uMinusOne=%#x uSyslevelFileVer=%#x achSignature=%.8Rhxs offTable=%#x vs cbRead=%#zx\n",
1793 pHdr->uMinusOne, pHdr->uSyslevelFileVer, pHdr->achSignature, pHdr->offTable, cbRead));
1794 }
1795 else
1796 LogRel(("Unattended: failed to read SYSLEVEL.OS2: %Rrc\n", vrc));
1797 }
1798 else
1799 LogRel(("Unattended: failed to open '%s' for reading: %Rrc\n", pBuf->sz, vrc));
1800 }
1801 }
1802 }
1803
1804 /** @todo language detection? */
1805
1806 /*
1807 * Only tested ACP2, so only return S_OK for it.
1808 */
1809 if ( *penmOsType == VBOXOSTYPE_OS2Warp45
1810 && RTStrVersionCompare(mStrDetectedOSVersion.c_str(), "4.52") >= 0
1811 && mStrDetectedOSFlavor.contains("Server", RTCString::CaseInsensitive))
1812 return S_OK;
1813
1814 return S_FALSE;
1815}
1816
1817
1818HRESULT Unattended::prepare()
1819{
1820 LogFlow(("Unattended::prepare: enter\n"));
1821
1822 /*
1823 * Must have a machine.
1824 */
1825 ComPtr<Machine> ptrMachine;
1826 Guid MachineUuid;
1827 {
1828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1829 ptrMachine = mMachine;
1830 if (ptrMachine.isNull())
1831 return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("No machine associated with this IUnatteded instance"));
1832 MachineUuid = mMachineUuid;
1833 }
1834
1835 /*
1836 * Before we write lock ourselves, we must get stuff from Machine and
1837 * VirtualBox because their locks have higher priorities than ours.
1838 */
1839 Utf8Str strGuestOsTypeId;
1840 Utf8Str strMachineName;
1841 Utf8Str strDefaultAuxBasePath;
1842 HRESULT hrc;
1843 try
1844 {
1845 Bstr bstrTmp;
1846 hrc = ptrMachine->COMGETTER(OSTypeId)(bstrTmp.asOutParam());
1847 if (SUCCEEDED(hrc))
1848 {
1849 strGuestOsTypeId = bstrTmp;
1850 hrc = ptrMachine->COMGETTER(Name)(bstrTmp.asOutParam());
1851 if (SUCCEEDED(hrc))
1852 strMachineName = bstrTmp;
1853 }
1854 int vrc = ptrMachine->i_calculateFullPath(Utf8StrFmt("Unattended-%RTuuid-", MachineUuid.raw()), strDefaultAuxBasePath);
1855 if (RT_FAILURE(vrc))
1856 return setErrorBoth(E_FAIL, vrc);
1857 }
1858 catch (std::bad_alloc &)
1859 {
1860 return E_OUTOFMEMORY;
1861 }
1862 bool const fIs64Bit = i_isGuestOSArchX64(strGuestOsTypeId);
1863
1864 BOOL fRtcUseUtc = FALSE;
1865 hrc = ptrMachine->COMGETTER(RTCUseUTC)(&fRtcUseUtc);
1866 if (FAILED(hrc))
1867 return hrc;
1868
1869 FirmwareType_T enmFirmware = FirmwareType_BIOS;
1870 hrc = ptrMachine->COMGETTER(FirmwareType)(&enmFirmware);
1871 if (FAILED(hrc))
1872 return hrc;
1873
1874 /*
1875 * Write lock this object and set attributes we got from IMachine.
1876 */
1877 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1878
1879 mStrGuestOsTypeId = strGuestOsTypeId;
1880 mfGuestOs64Bit = fIs64Bit;
1881 mfRtcUseUtc = RT_BOOL(fRtcUseUtc);
1882 menmFirmwareType = enmFirmware;
1883
1884 /*
1885 * Do some state checks.
1886 */
1887 if (mpInstaller != NULL)
1888 return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("The prepare method has been called (must call done to restart)"));
1889 if ((Machine *)ptrMachine != (Machine *)mMachine)
1890 return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("The 'machine' while we were using it - please don't do that"));
1891
1892 /*
1893 * Check if the specified ISOs and files exist.
1894 */
1895 if (!RTFileExists(mStrIsoPath.c_str()))
1896 return setErrorBoth(E_FAIL, VERR_FILE_NOT_FOUND, tr("Could not locate the installation ISO file '%s'"),
1897 mStrIsoPath.c_str());
1898 if (mfInstallGuestAdditions && !RTFileExists(mStrAdditionsIsoPath.c_str()))
1899 return setErrorBoth(E_FAIL, VERR_FILE_NOT_FOUND, tr("Could not locate the Guest Additions ISO file '%s'"),
1900 mStrAdditionsIsoPath.c_str());
1901 if (mfInstallTestExecService && !RTFileExists(mStrValidationKitIsoPath.c_str()))
1902 return setErrorBoth(E_FAIL, VERR_FILE_NOT_FOUND, tr("Could not locate the validation kit ISO file '%s'"),
1903 mStrValidationKitIsoPath.c_str());
1904 if (mStrScriptTemplatePath.isNotEmpty() && !RTFileExists(mStrScriptTemplatePath.c_str()))
1905 return setErrorBoth(E_FAIL, VERR_FILE_NOT_FOUND, tr("Could not locate unattended installation script template '%s'"),
1906 mStrScriptTemplatePath.c_str());
1907
1908 /*
1909 * Do media detection if it haven't been done yet.
1910 */
1911 if (!mfDoneDetectIsoOS)
1912 {
1913 hrc = detectIsoOS();
1914 if (FAILED(hrc) && hrc != E_NOTIMPL)
1915 return hrc;
1916 }
1917
1918 /*
1919 * Get the ISO's detect guest OS type info and make it's a known one (just
1920 * in case the above step doesn't work right).
1921 */
1922 uint32_t const idxIsoOSType = Global::getOSTypeIndexFromId(mStrDetectedOSTypeId.c_str());
1923 VBOXOSTYPE const enmIsoOSType = idxIsoOSType < Global::cOSTypes ? Global::sOSTypes[idxIsoOSType].osType : VBOXOSTYPE_Unknown;
1924 if ((enmIsoOSType & VBOXOSTYPE_OsTypeMask) == VBOXOSTYPE_Unknown)
1925 return setError(E_FAIL, tr("The supplied ISO file does not contain an OS currently supported for unattended installation"));
1926
1927 /*
1928 * Get the VM's configured guest OS type info.
1929 */
1930 uint32_t const idxMachineOSType = Global::getOSTypeIndexFromId(mStrGuestOsTypeId.c_str());
1931 VBOXOSTYPE const enmMachineOSType = idxMachineOSType < Global::cOSTypes
1932 ? Global::sOSTypes[idxMachineOSType].osType : VBOXOSTYPE_Unknown;
1933
1934 /*
1935 * Check that the detected guest OS type for the ISO is compatible with
1936 * that of the VM, boardly speaking.
1937 */
1938 if (idxMachineOSType != idxIsoOSType)
1939 {
1940 /* Check that the architecture is compatible: */
1941 if ( (enmIsoOSType & VBOXOSTYPE_ArchitectureMask) != (enmMachineOSType & VBOXOSTYPE_ArchitectureMask)
1942 && ( (enmIsoOSType & VBOXOSTYPE_ArchitectureMask) != VBOXOSTYPE_x86
1943 || (enmMachineOSType & VBOXOSTYPE_ArchitectureMask) != VBOXOSTYPE_x64))
1944 return setError(E_FAIL, tr("The supplied ISO file is incompatible with the guest OS type of the VM: CPU architecture mismatch"));
1945
1946 /** @todo check BIOS/EFI requirement */
1947 }
1948
1949 /*
1950 * Do some default property stuff and check other properties.
1951 */
1952 try
1953 {
1954 char szTmp[128];
1955
1956 if (mStrLocale.isEmpty())
1957 {
1958 int vrc = RTLocaleQueryNormalizedBaseLocaleName(szTmp, sizeof(szTmp));
1959 if ( RT_SUCCESS(vrc)
1960 && RTLOCALE_IS_LANGUAGE2_UNDERSCORE_COUNTRY2(szTmp))
1961 mStrLocale.assign(szTmp, 5);
1962 else
1963 mStrLocale = "en_US";
1964 Assert(RTLOCALE_IS_LANGUAGE2_UNDERSCORE_COUNTRY2(mStrLocale));
1965 }
1966
1967 if (mStrLanguage.isEmpty())
1968 {
1969 if (mDetectedOSLanguages.size() > 0)
1970 mStrLanguage = mDetectedOSLanguages[0];
1971 else
1972 mStrLanguage.assign(mStrLocale).findReplace('_', '-');
1973 }
1974
1975 if (mStrCountry.isEmpty())
1976 {
1977 int vrc = RTLocaleQueryUserCountryCode(szTmp);
1978 if (RT_SUCCESS(vrc))
1979 mStrCountry = szTmp;
1980 else if ( mStrLocale.isNotEmpty()
1981 && RTLOCALE_IS_LANGUAGE2_UNDERSCORE_COUNTRY2(mStrLocale))
1982 mStrCountry.assign(mStrLocale, 3, 2);
1983 else
1984 mStrCountry = "US";
1985 }
1986
1987 if (mStrTimeZone.isEmpty())
1988 {
1989 int vrc = RTTimeZoneGetCurrent(szTmp, sizeof(szTmp));
1990 if ( RT_SUCCESS(vrc)
1991 && strcmp(szTmp, "localtime") != 0 /* Typcial solaris TZ that isn't very helpful. */)
1992 mStrTimeZone = szTmp;
1993 else
1994 mStrTimeZone = "Etc/UTC";
1995 Assert(mStrTimeZone.isNotEmpty());
1996 }
1997 mpTimeZoneInfo = RTTimeZoneGetInfoByUnixName(mStrTimeZone.c_str());
1998 if (!mpTimeZoneInfo)
1999 mpTimeZoneInfo = RTTimeZoneGetInfoByWindowsName(mStrTimeZone.c_str());
2000 Assert(mpTimeZoneInfo || mStrTimeZone != "Etc/UTC");
2001 if (!mpTimeZoneInfo)
2002 LogRel(("Unattended::prepare: warning: Unknown time zone '%s'\n", mStrTimeZone.c_str()));
2003
2004 if (mStrHostname.isEmpty())
2005 {
2006 /* Mangle the VM name into a valid hostname. */
2007 for (size_t i = 0; i < strMachineName.length(); i++)
2008 {
2009 char ch = strMachineName[i];
2010 if ( (unsigned)ch < 127
2011 && RT_C_IS_ALNUM(ch))
2012 mStrHostname.append(ch);
2013 else if (mStrHostname.isNotEmpty() && RT_C_IS_PUNCT(ch) && !mStrHostname.endsWith("-"))
2014 mStrHostname.append('-');
2015 }
2016 if (mStrHostname.length() == 0)
2017 mStrHostname.printf("%RTuuid-vm", MachineUuid.raw());
2018 else if (mStrHostname.length() < 3)
2019 mStrHostname.append("-vm");
2020 mStrHostname.append(".myguest.virtualbox.org");
2021 }
2022
2023 if (mStrAuxiliaryBasePath.isEmpty())
2024 {
2025 mStrAuxiliaryBasePath = strDefaultAuxBasePath;
2026 mfIsDefaultAuxiliaryBasePath = true;
2027 }
2028 }
2029 catch (std::bad_alloc &)
2030 {
2031 return E_OUTOFMEMORY;
2032 }
2033
2034 /*
2035 * Instatiate the guest installer matching the ISO.
2036 */
2037 mpInstaller = UnattendedInstaller::createInstance(enmIsoOSType, mStrDetectedOSTypeId, mStrDetectedOSVersion,
2038 mStrDetectedOSFlavor, mStrDetectedOSHints, this);
2039 if (mpInstaller != NULL)
2040 {
2041 hrc = mpInstaller->initInstaller();
2042 if (SUCCEEDED(hrc))
2043 {
2044 /*
2045 * Do the script preps (just reads them).
2046 */
2047 hrc = mpInstaller->prepareUnattendedScripts();
2048 if (SUCCEEDED(hrc))
2049 {
2050 LogFlow(("Unattended::prepare: returns S_OK\n"));
2051 return S_OK;
2052 }
2053 }
2054
2055 /* Destroy the installer instance. */
2056 delete mpInstaller;
2057 mpInstaller = NULL;
2058 }
2059 else
2060 hrc = setErrorBoth(E_FAIL, VERR_NOT_FOUND,
2061 tr("Unattended installation is not supported for guest type '%s'"), mStrGuestOsTypeId.c_str());
2062 LogRelFlow(("Unattended::prepare: failed with %Rhrc\n", hrc));
2063 return hrc;
2064}
2065
2066HRESULT Unattended::constructMedia()
2067{
2068 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2069
2070 LogFlow(("===========================================================\n"));
2071 LogFlow(("Call Unattended::constructMedia()\n"));
2072
2073 if (mpInstaller == NULL)
2074 return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, "prepare() not yet called");
2075
2076 return mpInstaller->prepareMedia();
2077}
2078
2079HRESULT Unattended::reconfigureVM()
2080{
2081 LogFlow(("===========================================================\n"));
2082 LogFlow(("Call Unattended::reconfigureVM()\n"));
2083
2084 /*
2085 * Interrogate VirtualBox/IGuestOSType before we lock stuff and create ordering issues.
2086 */
2087 StorageBus_T enmRecommendedStorageBus = StorageBus_IDE;
2088 {
2089 Bstr bstrGuestOsTypeId;
2090 Bstr bstrDetectedOSTypeId;
2091 {
2092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2093 if (mpInstaller == NULL)
2094 return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("prepare() not yet called"));
2095 bstrGuestOsTypeId = mStrGuestOsTypeId;
2096 bstrDetectedOSTypeId = mStrDetectedOSTypeId;
2097 }
2098 ComPtr<IGuestOSType> ptrGuestOSType;
2099 HRESULT hrc = mParent->GetGuestOSType(bstrGuestOsTypeId.raw(), ptrGuestOSType.asOutParam());
2100 if (SUCCEEDED(hrc))
2101 {
2102 if (!ptrGuestOSType.isNull())
2103 hrc = ptrGuestOSType->COMGETTER(RecommendedDVDStorageBus)(&enmRecommendedStorageBus);
2104 }
2105 if (FAILED(hrc))
2106 return hrc;
2107
2108 /* If the detected guest OS type differs, log a warning if their DVD storage
2109 bus recommendations differ. */
2110 if (bstrGuestOsTypeId != bstrDetectedOSTypeId)
2111 {
2112 StorageBus_T enmRecommendedStorageBus2 = StorageBus_IDE;
2113 hrc = mParent->GetGuestOSType(bstrDetectedOSTypeId.raw(), ptrGuestOSType.asOutParam());
2114 if (SUCCEEDED(hrc) && !ptrGuestOSType.isNull())
2115 hrc = ptrGuestOSType->COMGETTER(RecommendedDVDStorageBus)(&enmRecommendedStorageBus2);
2116 if (FAILED(hrc))
2117 return hrc;
2118
2119 if (enmRecommendedStorageBus != enmRecommendedStorageBus2)
2120 LogRel(("Unattended::reconfigureVM: DVD storage bus recommendations differs for the VM and the ISO guest OS types: VM: %s (%ls), ISO: %s (%ls)\n",
2121 ::stringifyStorageBus(enmRecommendedStorageBus), bstrGuestOsTypeId.raw(),
2122 ::stringifyStorageBus(enmRecommendedStorageBus2), bstrDetectedOSTypeId.raw() ));
2123 }
2124 }
2125
2126 /*
2127 * Take write lock (for lock order reasons, write lock our parent object too)
2128 * then make sure we're the only caller of this method.
2129 */
2130 AutoMultiWriteLock2 alock(mMachine, this COMMA_LOCKVAL_SRC_POS);
2131 HRESULT hrc;
2132 if (mhThreadReconfigureVM == NIL_RTNATIVETHREAD)
2133 {
2134 RTNATIVETHREAD const hNativeSelf = RTThreadNativeSelf();
2135 mhThreadReconfigureVM = hNativeSelf;
2136
2137 /*
2138 * Create a new session, lock the machine and get the session machine object.
2139 * Do the locking without pinning down the write locks, just to be on the safe side.
2140 */
2141 ComPtr<ISession> ptrSession;
2142 try
2143 {
2144 hrc = ptrSession.createInprocObject(CLSID_Session);
2145 }
2146 catch (std::bad_alloc &)
2147 {
2148 hrc = E_OUTOFMEMORY;
2149 }
2150 if (SUCCEEDED(hrc))
2151 {
2152 alock.release();
2153 hrc = mMachine->LockMachine(ptrSession, LockType_Shared);
2154 alock.acquire();
2155 if (SUCCEEDED(hrc))
2156 {
2157 ComPtr<IMachine> ptrSessionMachine;
2158 hrc = ptrSession->COMGETTER(Machine)(ptrSessionMachine.asOutParam());
2159 if (SUCCEEDED(hrc))
2160 {
2161 /*
2162 * Hand the session to the inner work and let it do it job.
2163 */
2164 try
2165 {
2166 hrc = i_innerReconfigureVM(alock, enmRecommendedStorageBus, ptrSessionMachine);
2167 }
2168 catch (...)
2169 {
2170 hrc = E_UNEXPECTED;
2171 }
2172 }
2173
2174 /* Paranoia: release early in case we it a bump below. */
2175 Assert(mhThreadReconfigureVM == hNativeSelf);
2176 mhThreadReconfigureVM = NIL_RTNATIVETHREAD;
2177
2178 /*
2179 * While unlocking the machine we'll have to drop the locks again.
2180 */
2181 alock.release();
2182
2183 ptrSessionMachine.setNull();
2184 HRESULT hrc2 = ptrSession->UnlockMachine();
2185 AssertLogRelMsg(SUCCEEDED(hrc2), ("UnlockMachine -> %Rhrc\n", hrc2));
2186
2187 ptrSession.setNull();
2188
2189 alock.acquire();
2190 }
2191 else
2192 mhThreadReconfigureVM = NIL_RTNATIVETHREAD;
2193 }
2194 else
2195 mhThreadReconfigureVM = NIL_RTNATIVETHREAD;
2196 }
2197 else
2198 hrc = setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("reconfigureVM running on other thread"));
2199 return hrc;
2200}
2201
2202
2203HRESULT Unattended::i_innerReconfigureVM(AutoMultiWriteLock2 &rAutoLock, StorageBus_T enmRecommendedStorageBus,
2204 ComPtr<IMachine> const &rPtrSessionMachine)
2205{
2206 if (mpInstaller == NULL)
2207 return setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("prepare() not yet called"));
2208
2209 // Fetch all available storage controllers
2210 com::SafeIfaceArray<IStorageController> arrayOfControllers;
2211 HRESULT hrc = rPtrSessionMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(arrayOfControllers));
2212 AssertComRCReturn(hrc, hrc);
2213
2214 /*
2215 * Figure out where the images are to be mounted, adding controllers/ports as needed.
2216 */
2217 std::vector<UnattendedInstallationDisk> vecInstallationDisks;
2218 if (mpInstaller->isAuxiliaryFloppyNeeded())
2219 {
2220 hrc = i_reconfigureFloppy(arrayOfControllers, vecInstallationDisks, rPtrSessionMachine, rAutoLock);
2221 if (FAILED(hrc))
2222 return hrc;
2223 }
2224
2225 hrc = i_reconfigureIsos(arrayOfControllers, vecInstallationDisks, rPtrSessionMachine, rAutoLock, enmRecommendedStorageBus);
2226 if (FAILED(hrc))
2227 return hrc;
2228
2229 /*
2230 * Mount the images.
2231 */
2232 for (size_t idxImage = 0; idxImage < vecInstallationDisks.size(); idxImage++)
2233 {
2234 UnattendedInstallationDisk const *pImage = &vecInstallationDisks.at(idxImage);
2235 Assert(pImage->strImagePath.isNotEmpty());
2236 hrc = i_attachImage(pImage, rPtrSessionMachine, rAutoLock);
2237 if (FAILED(hrc))
2238 return hrc;
2239 }
2240
2241 /*
2242 * Set the boot order.
2243 *
2244 * ASSUME that the HD isn't bootable when we start out, but it will be what
2245 * we boot from after the first stage of the installation is done. Setting
2246 * it first prevents endless reboot cylces.
2247 */
2248 /** @todo consider making 100% sure the disk isn't bootable (edit partition
2249 * table active bits and EFI stuff). */
2250 Assert( mpInstaller->getBootableDeviceType() == DeviceType_DVD
2251 || mpInstaller->getBootableDeviceType() == DeviceType_Floppy);
2252 hrc = rPtrSessionMachine->SetBootOrder(1, DeviceType_HardDisk);
2253 if (SUCCEEDED(hrc))
2254 hrc = rPtrSessionMachine->SetBootOrder(2, mpInstaller->getBootableDeviceType());
2255 if (SUCCEEDED(hrc))
2256 hrc = rPtrSessionMachine->SetBootOrder(3, mpInstaller->getBootableDeviceType() == DeviceType_DVD
2257 ? DeviceType_Floppy : DeviceType_DVD);
2258 if (FAILED(hrc))
2259 return hrc;
2260
2261 /*
2262 * Essential step.
2263 *
2264 * HACK ALERT! We have to release the lock here or we'll get into trouble with
2265 * the VirtualBox lock (via i_saveHardware/NetworkAdaptger::i_hasDefaults/VirtualBox::i_findGuestOSType).
2266 */
2267 if (SUCCEEDED(hrc))
2268 {
2269 rAutoLock.release();
2270 hrc = rPtrSessionMachine->SaveSettings();
2271 rAutoLock.acquire();
2272 }
2273
2274 return hrc;
2275}
2276
2277/**
2278 * Makes sure we've got a floppy drive attached to a floppy controller, adding
2279 * the auxiliary floppy image to the installation disk vector.
2280 *
2281 * @returns COM status code.
2282 * @param rControllers The existing controllers.
2283 * @param rVecInstallatationDisks The list of image to mount.
2284 * @param rPtrSessionMachine The session machine smart pointer.
2285 * @param rAutoLock The lock.
2286 */
2287HRESULT Unattended::i_reconfigureFloppy(com::SafeIfaceArray<IStorageController> &rControllers,
2288 std::vector<UnattendedInstallationDisk> &rVecInstallatationDisks,
2289 ComPtr<IMachine> const &rPtrSessionMachine,
2290 AutoMultiWriteLock2 &rAutoLock)
2291{
2292 Assert(mpInstaller->isAuxiliaryFloppyNeeded());
2293
2294 /*
2295 * Look for a floppy controller with a primary drive (A:) we can "insert"
2296 * the auxiliary floppy image. Add a controller and/or a drive if necessary.
2297 */
2298 bool fFoundPort0Dev0 = false;
2299 Bstr bstrControllerName;
2300 Utf8Str strControllerName;
2301
2302 for (size_t i = 0; i < rControllers.size(); ++i)
2303 {
2304 StorageBus_T enmStorageBus;
2305 HRESULT hrc = rControllers[i]->COMGETTER(Bus)(&enmStorageBus);
2306 AssertComRCReturn(hrc, hrc);
2307 if (enmStorageBus == StorageBus_Floppy)
2308 {
2309
2310 /*
2311 * Found a floppy controller.
2312 */
2313 hrc = rControllers[i]->COMGETTER(Name)(bstrControllerName.asOutParam());
2314 AssertComRCReturn(hrc, hrc);
2315
2316 /*
2317 * Check the attchments to see if we've got a device 0 attached on port 0.
2318 *
2319 * While we're at it we eject flppies from all floppy drives we encounter,
2320 * we don't want any confusion at boot or during installation.
2321 */
2322 com::SafeIfaceArray<IMediumAttachment> arrayOfMediumAttachments;
2323 hrc = rPtrSessionMachine->GetMediumAttachmentsOfController(bstrControllerName.raw(),
2324 ComSafeArrayAsOutParam(arrayOfMediumAttachments));
2325 AssertComRCReturn(hrc, hrc);
2326 strControllerName = bstrControllerName;
2327 AssertLogRelReturn(strControllerName.isNotEmpty(), setErrorBoth(E_UNEXPECTED, VERR_INTERNAL_ERROR_2));
2328
2329 for (size_t j = 0; j < arrayOfMediumAttachments.size(); j++)
2330 {
2331 LONG iPort = -1;
2332 hrc = arrayOfMediumAttachments[j]->COMGETTER(Port)(&iPort);
2333 AssertComRCReturn(hrc, hrc);
2334
2335 LONG iDevice = -1;
2336 hrc = arrayOfMediumAttachments[j]->COMGETTER(Device)(&iDevice);
2337 AssertComRCReturn(hrc, hrc);
2338
2339 DeviceType_T enmType;
2340 hrc = arrayOfMediumAttachments[j]->COMGETTER(Type)(&enmType);
2341 AssertComRCReturn(hrc, hrc);
2342
2343 if (enmType == DeviceType_Floppy)
2344 {
2345 ComPtr<IMedium> ptrMedium;
2346 hrc = arrayOfMediumAttachments[j]->COMGETTER(Medium)(ptrMedium.asOutParam());
2347 AssertComRCReturn(hrc, hrc);
2348
2349 if (ptrMedium.isNotNull())
2350 {
2351 ptrMedium.setNull();
2352 rAutoLock.release();
2353 hrc = rPtrSessionMachine->UnmountMedium(bstrControllerName.raw(), iPort, iDevice, TRUE /*fForce*/);
2354 rAutoLock.acquire();
2355 }
2356
2357 if (iPort == 0 && iDevice == 0)
2358 fFoundPort0Dev0 = true;
2359 }
2360 else if (iPort == 0 && iDevice == 0)
2361 return setError(E_FAIL,
2362 tr("Found non-floppy device attached to port 0 device 0 on the floppy controller '%ls'"),
2363 bstrControllerName.raw());
2364 }
2365 }
2366 }
2367
2368 /*
2369 * Add a floppy controller if we need to.
2370 */
2371 if (strControllerName.isEmpty())
2372 {
2373 bstrControllerName = strControllerName = "Floppy";
2374 ComPtr<IStorageController> ptrControllerIgnored;
2375 HRESULT hrc = rPtrSessionMachine->AddStorageController(bstrControllerName.raw(), StorageBus_Floppy,
2376 ptrControllerIgnored.asOutParam());
2377 LogRelFunc(("Machine::addStorageController(Floppy) -> %Rhrc \n", hrc));
2378 if (FAILED(hrc))
2379 return hrc;
2380 }
2381
2382 /*
2383 * Adding a floppy drive (if needed) and mounting the auxiliary image is
2384 * done later together with the ISOs.
2385 */
2386 rVecInstallatationDisks.push_back(UnattendedInstallationDisk(StorageBus_Floppy, strControllerName,
2387 DeviceType_Floppy, AccessMode_ReadWrite,
2388 0, 0,
2389 fFoundPort0Dev0 /*fMountOnly*/,
2390 mpInstaller->getAuxiliaryFloppyFilePath()));
2391 return S_OK;
2392}
2393
2394/**
2395 * Reconfigures DVD drives of the VM to mount all the ISOs we need.
2396 *
2397 * This will umount all DVD media.
2398 *
2399 * @returns COM status code.
2400 * @param rControllers The existing controllers.
2401 * @param rVecInstallatationDisks The list of image to mount.
2402 * @param rPtrSessionMachine The session machine smart pointer.
2403 * @param rAutoLock The lock.
2404 * @param enmRecommendedStorageBus The recommended storage bus type for adding
2405 * DVD drives on.
2406 */
2407HRESULT Unattended::i_reconfigureIsos(com::SafeIfaceArray<IStorageController> &rControllers,
2408 std::vector<UnattendedInstallationDisk> &rVecInstallatationDisks,
2409 ComPtr<IMachine> const &rPtrSessionMachine,
2410 AutoMultiWriteLock2 &rAutoLock, StorageBus_T enmRecommendedStorageBus)
2411{
2412 /*
2413 * Enumerate the attachements of every controller, looking for DVD drives,
2414 * ASSUMEING all drives are bootable.
2415 *
2416 * Eject the medium from all the drives (don't want any confusion) and look
2417 * for the recommended storage bus in case we need to add more drives.
2418 */
2419 HRESULT hrc;
2420 std::list<ControllerSlot> lstControllerDvdSlots;
2421 Utf8Str strRecommendedControllerName; /* non-empty if recommended bus found. */
2422 Utf8Str strControllerName;
2423 Bstr bstrControllerName;
2424 for (size_t i = 0; i < rControllers.size(); ++i)
2425 {
2426 hrc = rControllers[i]->COMGETTER(Name)(bstrControllerName.asOutParam());
2427 AssertComRCReturn(hrc, hrc);
2428 strControllerName = bstrControllerName;
2429
2430 /* Look for recommended storage bus. */
2431 StorageBus_T enmStorageBus;
2432 hrc = rControllers[i]->COMGETTER(Bus)(&enmStorageBus);
2433 AssertComRCReturn(hrc, hrc);
2434 if (enmStorageBus == enmRecommendedStorageBus)
2435 {
2436 strRecommendedControllerName = bstrControllerName;
2437 AssertLogRelReturn(strControllerName.isNotEmpty(), setErrorBoth(E_UNEXPECTED, VERR_INTERNAL_ERROR_2));
2438 }
2439
2440 /* Scan the controller attachments. */
2441 com::SafeIfaceArray<IMediumAttachment> arrayOfMediumAttachments;
2442 hrc = rPtrSessionMachine->GetMediumAttachmentsOfController(bstrControllerName.raw(),
2443 ComSafeArrayAsOutParam(arrayOfMediumAttachments));
2444 AssertComRCReturn(hrc, hrc);
2445
2446 for (size_t j = 0; j < arrayOfMediumAttachments.size(); j++)
2447 {
2448 DeviceType_T enmType;
2449 hrc = arrayOfMediumAttachments[j]->COMGETTER(Type)(&enmType);
2450 AssertComRCReturn(hrc, hrc);
2451 if (enmType == DeviceType_DVD)
2452 {
2453 LONG iPort = -1;
2454 hrc = arrayOfMediumAttachments[j]->COMGETTER(Port)(&iPort);
2455 AssertComRCReturn(hrc, hrc);
2456
2457 LONG iDevice = -1;
2458 hrc = arrayOfMediumAttachments[j]->COMGETTER(Device)(&iDevice);
2459 AssertComRCReturn(hrc, hrc);
2460
2461 /* Remeber it. */
2462 lstControllerDvdSlots.push_back(ControllerSlot(enmStorageBus, strControllerName, iPort, iDevice, false /*fFree*/));
2463
2464 /* Eject the medium, if any. */
2465 ComPtr<IMedium> ptrMedium;
2466 hrc = arrayOfMediumAttachments[j]->COMGETTER(Medium)(ptrMedium.asOutParam());
2467 AssertComRCReturn(hrc, hrc);
2468 if (ptrMedium.isNotNull())
2469 {
2470 ptrMedium.setNull();
2471
2472 rAutoLock.release();
2473 hrc = rPtrSessionMachine->UnmountMedium(bstrControllerName.raw(), iPort, iDevice, TRUE /*fForce*/);
2474 rAutoLock.acquire();
2475 }
2476 }
2477 }
2478 }
2479
2480 /*
2481 * How many drives do we need? Add more if necessary.
2482 */
2483 ULONG cDvdDrivesNeeded = 0;
2484 if (mpInstaller->isAuxiliaryIsoNeeded())
2485 cDvdDrivesNeeded++;
2486 if (mpInstaller->isOriginalIsoNeeded())
2487 cDvdDrivesNeeded++;
2488#if 0 /* These are now in the AUX VISO. */
2489 if (mpInstaller->isAdditionsIsoNeeded())
2490 cDvdDrivesNeeded++;
2491 if (mpInstaller->isValidationKitIsoNeeded())
2492 cDvdDrivesNeeded++;
2493#endif
2494 Assert(cDvdDrivesNeeded > 0);
2495 if (cDvdDrivesNeeded > lstControllerDvdSlots.size())
2496 {
2497 /* Do we need to add the recommended controller? */
2498 if (strRecommendedControllerName.isEmpty())
2499 {
2500 switch (enmRecommendedStorageBus)
2501 {
2502 case StorageBus_IDE: strRecommendedControllerName = "IDE"; break;
2503 case StorageBus_SATA: strRecommendedControllerName = "SATA"; break;
2504 case StorageBus_SCSI: strRecommendedControllerName = "SCSI"; break;
2505 case StorageBus_SAS: strRecommendedControllerName = "SAS"; break;
2506 case StorageBus_USB: strRecommendedControllerName = "USB"; break;
2507 case StorageBus_PCIe: strRecommendedControllerName = "PCIe"; break;
2508 default:
2509 return setError(E_FAIL, tr("Support for recommended storage bus %d not implemented"),
2510 (int)enmRecommendedStorageBus);
2511 }
2512 ComPtr<IStorageController> ptrControllerIgnored;
2513 hrc = rPtrSessionMachine->AddStorageController(Bstr(strRecommendedControllerName).raw(), enmRecommendedStorageBus,
2514 ptrControllerIgnored.asOutParam());
2515 LogRelFunc(("Machine::addStorageController(%s) -> %Rhrc \n", strRecommendedControllerName.c_str(), hrc));
2516 if (FAILED(hrc))
2517 return hrc;
2518 }
2519
2520 /* Add free controller slots, maybe raising the port limit on the controller if we can. */
2521 hrc = i_findOrCreateNeededFreeSlots(strRecommendedControllerName, enmRecommendedStorageBus, rPtrSessionMachine,
2522 cDvdDrivesNeeded, lstControllerDvdSlots);
2523 if (FAILED(hrc))
2524 return hrc;
2525 if (cDvdDrivesNeeded > lstControllerDvdSlots.size())
2526 {
2527 /* We could in many cases create another controller here, but it's not worth the effort. */
2528 return setError(E_FAIL, tr("Not enough free slots on controller '%s' to add %u DVD drive(s)", "",
2529 cDvdDrivesNeeded - lstControllerDvdSlots.size()),
2530 strRecommendedControllerName.c_str(), cDvdDrivesNeeded - lstControllerDvdSlots.size());
2531 }
2532 Assert(cDvdDrivesNeeded == lstControllerDvdSlots.size());
2533 }
2534
2535 /*
2536 * Sort the DVD slots in boot order.
2537 */
2538 lstControllerDvdSlots.sort();
2539
2540 /*
2541 * Prepare ISO mounts.
2542 *
2543 * Boot order depends on bootFromAuxiliaryIso() and we must grab DVD slots
2544 * according to the boot order.
2545 */
2546 std::list<ControllerSlot>::const_iterator itDvdSlot = lstControllerDvdSlots.begin();
2547 if (mpInstaller->isAuxiliaryIsoNeeded() && mpInstaller->bootFromAuxiliaryIso())
2548 {
2549 rVecInstallatationDisks.push_back(UnattendedInstallationDisk(itDvdSlot, mpInstaller->getAuxiliaryIsoFilePath()));
2550 ++itDvdSlot;
2551 }
2552
2553 if (mpInstaller->isOriginalIsoNeeded())
2554 {
2555 rVecInstallatationDisks.push_back(UnattendedInstallationDisk(itDvdSlot, i_getIsoPath()));
2556 ++itDvdSlot;
2557 }
2558
2559 if (mpInstaller->isAuxiliaryIsoNeeded() && !mpInstaller->bootFromAuxiliaryIso())
2560 {
2561 rVecInstallatationDisks.push_back(UnattendedInstallationDisk(itDvdSlot, mpInstaller->getAuxiliaryIsoFilePath()));
2562 ++itDvdSlot;
2563 }
2564
2565#if 0 /* These are now in the AUX VISO. */
2566 if (mpInstaller->isAdditionsIsoNeeded())
2567 {
2568 rVecInstallatationDisks.push_back(UnattendedInstallationDisk(itDvdSlot, i_getAdditionsIsoPath()));
2569 ++itDvdSlot;
2570 }
2571
2572 if (mpInstaller->isValidationKitIsoNeeded())
2573 {
2574 rVecInstallatationDisks.push_back(UnattendedInstallationDisk(itDvdSlot, i_getValidationKitIsoPath()));
2575 ++itDvdSlot;
2576 }
2577#endif
2578
2579 return S_OK;
2580}
2581
2582/**
2583 * Used to find more free slots for DVD drives during VM reconfiguration.
2584 *
2585 * This may modify the @a portCount property of the given controller.
2586 *
2587 * @returns COM status code.
2588 * @param rStrControllerName The name of the controller to find/create
2589 * free slots on.
2590 * @param enmStorageBus The storage bus type.
2591 * @param rPtrSessionMachine Reference to the session machine.
2592 * @param cSlotsNeeded Total slots needed (including those we've
2593 * already found).
2594 * @param rDvdSlots The slot collection for DVD drives to add
2595 * free slots to as we find/create them.
2596 */
2597HRESULT Unattended::i_findOrCreateNeededFreeSlots(const Utf8Str &rStrControllerName, StorageBus_T enmStorageBus,
2598 ComPtr<IMachine> const &rPtrSessionMachine, uint32_t cSlotsNeeded,
2599 std::list<ControllerSlot> &rDvdSlots)
2600{
2601 Assert(cSlotsNeeded > rDvdSlots.size());
2602
2603 /*
2604 * Get controlleer stats.
2605 */
2606 ComPtr<IStorageController> pController;
2607 HRESULT hrc = rPtrSessionMachine->GetStorageControllerByName(Bstr(rStrControllerName).raw(), pController.asOutParam());
2608 AssertComRCReturn(hrc, hrc);
2609
2610 ULONG cMaxDevicesPerPort = 1;
2611 hrc = pController->COMGETTER(MaxDevicesPerPortCount)(&cMaxDevicesPerPort);
2612 AssertComRCReturn(hrc, hrc);
2613 AssertLogRelReturn(cMaxDevicesPerPort > 0, E_UNEXPECTED);
2614
2615 ULONG cPorts = 0;
2616 hrc = pController->COMGETTER(PortCount)(&cPorts);
2617 AssertComRCReturn(hrc, hrc);
2618
2619 /*
2620 * Get the attachment list and turn into an internal list for lookup speed.
2621 */
2622 com::SafeIfaceArray<IMediumAttachment> arrayOfMediumAttachments;
2623 hrc = rPtrSessionMachine->GetMediumAttachmentsOfController(Bstr(rStrControllerName).raw(),
2624 ComSafeArrayAsOutParam(arrayOfMediumAttachments));
2625 AssertComRCReturn(hrc, hrc);
2626
2627 std::vector<ControllerSlot> arrayOfUsedSlots;
2628 for (size_t i = 0; i < arrayOfMediumAttachments.size(); i++)
2629 {
2630 LONG iPort = -1;
2631 hrc = arrayOfMediumAttachments[i]->COMGETTER(Port)(&iPort);
2632 AssertComRCReturn(hrc, hrc);
2633
2634 LONG iDevice = -1;
2635 hrc = arrayOfMediumAttachments[i]->COMGETTER(Device)(&iDevice);
2636 AssertComRCReturn(hrc, hrc);
2637
2638 arrayOfUsedSlots.push_back(ControllerSlot(enmStorageBus, Utf8Str::Empty, iPort, iDevice, false /*fFree*/));
2639 }
2640
2641 /*
2642 * Iterate thru all possible slots, adding those not found in arrayOfUsedSlots.
2643 */
2644 for (int32_t iPort = 0; iPort < (int32_t)cPorts; iPort++)
2645 for (int32_t iDevice = 0; iDevice < (int32_t)cMaxDevicesPerPort; iDevice++)
2646 {
2647 bool fFound = false;
2648 for (size_t i = 0; i < arrayOfUsedSlots.size(); i++)
2649 if ( arrayOfUsedSlots[i].iPort == iPort
2650 && arrayOfUsedSlots[i].iDevice == iDevice)
2651 {
2652 fFound = true;
2653 break;
2654 }
2655 if (!fFound)
2656 {
2657 rDvdSlots.push_back(ControllerSlot(enmStorageBus, rStrControllerName, iPort, iDevice, true /*fFree*/));
2658 if (rDvdSlots.size() >= cSlotsNeeded)
2659 return S_OK;
2660 }
2661 }
2662
2663 /*
2664 * Okay we still need more ports. See if increasing the number of controller
2665 * ports would solve it.
2666 */
2667 ULONG cMaxPorts = 1;
2668 hrc = pController->COMGETTER(MaxPortCount)(&cMaxPorts);
2669 AssertComRCReturn(hrc, hrc);
2670 if (cMaxPorts <= cPorts)
2671 return S_OK;
2672 size_t cNewPortsNeeded = (cSlotsNeeded - rDvdSlots.size() + cMaxDevicesPerPort - 1) / cMaxDevicesPerPort;
2673 if (cPorts + cNewPortsNeeded > cMaxPorts)
2674 return S_OK;
2675
2676 /*
2677 * Raise the port count and add the free slots we've just created.
2678 */
2679 hrc = pController->COMSETTER(PortCount)(cPorts + (ULONG)cNewPortsNeeded);
2680 AssertComRCReturn(hrc, hrc);
2681 int32_t const cPortsNew = (int32_t)(cPorts + cNewPortsNeeded);
2682 for (int32_t iPort = (int32_t)cPorts; iPort < cPortsNew; iPort++)
2683 for (int32_t iDevice = 0; iDevice < (int32_t)cMaxDevicesPerPort; iDevice++)
2684 {
2685 rDvdSlots.push_back(ControllerSlot(enmStorageBus, rStrControllerName, iPort, iDevice, true /*fFree*/));
2686 if (rDvdSlots.size() >= cSlotsNeeded)
2687 return S_OK;
2688 }
2689
2690 /* We should not get here! */
2691 AssertLogRelFailedReturn(E_UNEXPECTED);
2692}
2693
2694HRESULT Unattended::done()
2695{
2696 LogFlow(("Unattended::done\n"));
2697 if (mpInstaller)
2698 {
2699 LogRelFlow(("Unattended::done: Deleting installer object (%p)\n", mpInstaller));
2700 delete mpInstaller;
2701 mpInstaller = NULL;
2702 }
2703 return S_OK;
2704}
2705
2706HRESULT Unattended::getIsoPath(com::Utf8Str &isoPath)
2707{
2708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2709 isoPath = mStrIsoPath;
2710 return S_OK;
2711}
2712
2713HRESULT Unattended::setIsoPath(const com::Utf8Str &isoPath)
2714{
2715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2716 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2717 mStrIsoPath = isoPath;
2718 mfDoneDetectIsoOS = false;
2719 return S_OK;
2720}
2721
2722HRESULT Unattended::getUser(com::Utf8Str &user)
2723{
2724 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2725 user = mStrUser;
2726 return S_OK;
2727}
2728
2729
2730HRESULT Unattended::setUser(const com::Utf8Str &user)
2731{
2732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2733 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2734 mStrUser = user;
2735 return S_OK;
2736}
2737
2738HRESULT Unattended::getPassword(com::Utf8Str &password)
2739{
2740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2741 password = mStrPassword;
2742 return S_OK;
2743}
2744
2745HRESULT Unattended::setPassword(const com::Utf8Str &password)
2746{
2747 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2748 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2749 mStrPassword = password;
2750 return S_OK;
2751}
2752
2753HRESULT Unattended::getFullUserName(com::Utf8Str &fullUserName)
2754{
2755 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2756 fullUserName = mStrFullUserName;
2757 return S_OK;
2758}
2759
2760HRESULT Unattended::setFullUserName(const com::Utf8Str &fullUserName)
2761{
2762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2763 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2764 mStrFullUserName = fullUserName;
2765 return S_OK;
2766}
2767
2768HRESULT Unattended::getProductKey(com::Utf8Str &productKey)
2769{
2770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2771 productKey = mStrProductKey;
2772 return S_OK;
2773}
2774
2775HRESULT Unattended::setProductKey(const com::Utf8Str &productKey)
2776{
2777 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2778 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2779 mStrProductKey = productKey;
2780 return S_OK;
2781}
2782
2783HRESULT Unattended::getAdditionsIsoPath(com::Utf8Str &additionsIsoPath)
2784{
2785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2786 additionsIsoPath = mStrAdditionsIsoPath;
2787 return S_OK;
2788}
2789
2790HRESULT Unattended::setAdditionsIsoPath(const com::Utf8Str &additionsIsoPath)
2791{
2792 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2793 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2794 mStrAdditionsIsoPath = additionsIsoPath;
2795 return S_OK;
2796}
2797
2798HRESULT Unattended::getInstallGuestAdditions(BOOL *installGuestAdditions)
2799{
2800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2801 *installGuestAdditions = mfInstallGuestAdditions;
2802 return S_OK;
2803}
2804
2805HRESULT Unattended::setInstallGuestAdditions(BOOL installGuestAdditions)
2806{
2807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2808 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2809 mfInstallGuestAdditions = installGuestAdditions != FALSE;
2810 return S_OK;
2811}
2812
2813HRESULT Unattended::getValidationKitIsoPath(com::Utf8Str &aValidationKitIsoPath)
2814{
2815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2816 aValidationKitIsoPath = mStrValidationKitIsoPath;
2817 return S_OK;
2818}
2819
2820HRESULT Unattended::setValidationKitIsoPath(const com::Utf8Str &aValidationKitIsoPath)
2821{
2822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2823 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2824 mStrValidationKitIsoPath = aValidationKitIsoPath;
2825 return S_OK;
2826}
2827
2828HRESULT Unattended::getInstallTestExecService(BOOL *aInstallTestExecService)
2829{
2830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2831 *aInstallTestExecService = mfInstallTestExecService;
2832 return S_OK;
2833}
2834
2835HRESULT Unattended::setInstallTestExecService(BOOL aInstallTestExecService)
2836{
2837 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2838 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2839 mfInstallTestExecService = aInstallTestExecService != FALSE;
2840 return S_OK;
2841}
2842
2843HRESULT Unattended::getTimeZone(com::Utf8Str &aTimeZone)
2844{
2845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2846 aTimeZone = mStrTimeZone;
2847 return S_OK;
2848}
2849
2850HRESULT Unattended::setTimeZone(const com::Utf8Str &aTimezone)
2851{
2852 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2853 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2854 mStrTimeZone = aTimezone;
2855 return S_OK;
2856}
2857
2858HRESULT Unattended::getLocale(com::Utf8Str &aLocale)
2859{
2860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2861 aLocale = mStrLocale;
2862 return S_OK;
2863}
2864
2865HRESULT Unattended::setLocale(const com::Utf8Str &aLocale)
2866{
2867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2868 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2869 if ( aLocale.isEmpty() /* use default */
2870 || ( aLocale.length() == 5
2871 && RT_C_IS_LOWER(aLocale[0])
2872 && RT_C_IS_LOWER(aLocale[1])
2873 && aLocale[2] == '_'
2874 && RT_C_IS_UPPER(aLocale[3])
2875 && RT_C_IS_UPPER(aLocale[4])) )
2876 {
2877 mStrLocale = aLocale;
2878 return S_OK;
2879 }
2880 return setError(E_INVALIDARG, tr("Expected two lower cased letters, an underscore, and two upper cased letters"));
2881}
2882
2883HRESULT Unattended::getLanguage(com::Utf8Str &aLanguage)
2884{
2885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2886 aLanguage = mStrLanguage;
2887 return S_OK;
2888}
2889
2890HRESULT Unattended::setLanguage(const com::Utf8Str &aLanguage)
2891{
2892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2893 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2894 mStrLanguage = aLanguage;
2895 return S_OK;
2896}
2897
2898HRESULT Unattended::getCountry(com::Utf8Str &aCountry)
2899{
2900 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2901 aCountry = mStrCountry;
2902 return S_OK;
2903}
2904
2905HRESULT Unattended::setCountry(const com::Utf8Str &aCountry)
2906{
2907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2908 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2909 if ( aCountry.isEmpty()
2910 || ( aCountry.length() == 2
2911 && RT_C_IS_UPPER(aCountry[0])
2912 && RT_C_IS_UPPER(aCountry[1])) )
2913 {
2914 mStrCountry = aCountry;
2915 return S_OK;
2916 }
2917 return setError(E_INVALIDARG, tr("Expected two upper cased letters"));
2918}
2919
2920HRESULT Unattended::getProxy(com::Utf8Str &aProxy)
2921{
2922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2923 aProxy = mStrProxy; /// @todo turn schema map into string or something.
2924 return S_OK;
2925}
2926
2927HRESULT Unattended::setProxy(const com::Utf8Str &aProxy)
2928{
2929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2930 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2931 if (aProxy.isEmpty())
2932 {
2933 /* set default proxy */
2934 /** @todo BUGBUG! implement this */
2935 }
2936 else if (aProxy.equalsIgnoreCase("none"))
2937 {
2938 /* clear proxy config */
2939 mStrProxy.setNull();
2940 }
2941 else
2942 {
2943 /** @todo Parse and set proxy config into a schema map or something along those lines. */
2944 /** @todo BUGBUG! implement this */
2945 // return E_NOTIMPL;
2946 mStrProxy = aProxy;
2947 }
2948 return S_OK;
2949}
2950
2951HRESULT Unattended::getPackageSelectionAdjustments(com::Utf8Str &aPackageSelectionAdjustments)
2952{
2953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2954 aPackageSelectionAdjustments = RTCString::join(mPackageSelectionAdjustments, ";");
2955 return S_OK;
2956}
2957
2958HRESULT Unattended::setPackageSelectionAdjustments(const com::Utf8Str &aPackageSelectionAdjustments)
2959{
2960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2961 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
2962 if (aPackageSelectionAdjustments.isEmpty())
2963 mPackageSelectionAdjustments.clear();
2964 else
2965 {
2966 RTCList<RTCString, RTCString *> arrayStrSplit = aPackageSelectionAdjustments.split(";");
2967 for (size_t i = 0; i < arrayStrSplit.size(); i++)
2968 {
2969 if (arrayStrSplit[i].equals("minimal"))
2970 { /* okay */ }
2971 else
2972 return setError(E_INVALIDARG, tr("Unknown keyword: %s"), arrayStrSplit[i].c_str());
2973 }
2974 mPackageSelectionAdjustments = arrayStrSplit;
2975 }
2976 return S_OK;
2977}
2978
2979HRESULT Unattended::getHostname(com::Utf8Str &aHostname)
2980{
2981 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2982 aHostname = mStrHostname;
2983 return S_OK;
2984}
2985
2986HRESULT Unattended::setHostname(const com::Utf8Str &aHostname)
2987{
2988 /*
2989 * Validate input.
2990 */
2991 if (aHostname.length() > (aHostname.endsWith(".") ? 254U : 253U))
2992 return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
2993 tr("Hostname '%s' is %zu bytes long, max is 253 (excluding trailing dot)", "", aHostname.length()),
2994 aHostname.c_str(), aHostname.length());
2995 size_t cLabels = 0;
2996 const char *pszSrc = aHostname.c_str();
2997 for (;;)
2998 {
2999 size_t cchLabel = 1;
3000 char ch = *pszSrc++;
3001 if (RT_C_IS_ALNUM(ch))
3002 {
3003 cLabels++;
3004 while ((ch = *pszSrc++) != '.' && ch != '\0')
3005 {
3006 if (RT_C_IS_ALNUM(ch) || ch == '-')
3007 {
3008 if (cchLabel < 63)
3009 cchLabel++;
3010 else
3011 return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
3012 tr("Invalid hostname '%s' - label %u is too long, max is 63."),
3013 aHostname.c_str(), cLabels);
3014 }
3015 else
3016 return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
3017 tr("Invalid hostname '%s' - illegal char '%c' at position %zu"),
3018 aHostname.c_str(), ch, pszSrc - aHostname.c_str() - 1);
3019 }
3020 if (cLabels == 1 && cchLabel < 2)
3021 return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
3022 tr("Invalid hostname '%s' - the name part must be at least two characters long"),
3023 aHostname.c_str());
3024 if (ch == '\0')
3025 break;
3026 }
3027 else if (ch != '\0')
3028 return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
3029 tr("Invalid hostname '%s' - illegal lead char '%c' at position %zu"),
3030 aHostname.c_str(), ch, pszSrc - aHostname.c_str() - 1);
3031 else
3032 return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
3033 tr("Invalid hostname '%s' - trailing dot not permitted"), aHostname.c_str());
3034 }
3035 if (cLabels < 2)
3036 return setErrorBoth(E_INVALIDARG, VERR_INVALID_NAME,
3037 tr("Incomplete hostname '%s' - must include both a name and a domain"), aHostname.c_str());
3038
3039 /*
3040 * Make the change.
3041 */
3042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3043 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
3044 mStrHostname = aHostname;
3045 return S_OK;
3046}
3047
3048HRESULT Unattended::getAuxiliaryBasePath(com::Utf8Str &aAuxiliaryBasePath)
3049{
3050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3051 aAuxiliaryBasePath = mStrAuxiliaryBasePath;
3052 return S_OK;
3053}
3054
3055HRESULT Unattended::setAuxiliaryBasePath(const com::Utf8Str &aAuxiliaryBasePath)
3056{
3057 if (aAuxiliaryBasePath.isEmpty())
3058 return setError(E_INVALIDARG, tr("Empty base path is not allowed"));
3059 if (!RTPathStartsWithRoot(aAuxiliaryBasePath.c_str()))
3060 return setError(E_INVALIDARG, tr("Base path must be absolute"));
3061
3062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3063 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
3064 mStrAuxiliaryBasePath = aAuxiliaryBasePath;
3065 mfIsDefaultAuxiliaryBasePath = mStrAuxiliaryBasePath.isEmpty();
3066 return S_OK;
3067}
3068
3069HRESULT Unattended::getImageIndex(ULONG *index)
3070{
3071 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3072 *index = midxImage;
3073 return S_OK;
3074}
3075
3076HRESULT Unattended::setImageIndex(ULONG index)
3077{
3078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3079 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
3080 /* Set midxImage only if mDetectedImages includes an image with an index equal to @param index. */
3081 for (size_t i = 0; i < mDetectedImages.size(); ++i)
3082 {
3083 if (mDetectedImages[i].mImageIndex == index)
3084 {
3085 midxImage = index;
3086 return S_OK;
3087 }
3088 }
3089 return E_INVALIDARG;
3090}
3091
3092HRESULT Unattended::getMachine(ComPtr<IMachine> &aMachine)
3093{
3094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3095 return mMachine.queryInterfaceTo(aMachine.asOutParam());
3096}
3097
3098HRESULT Unattended::setMachine(const ComPtr<IMachine> &aMachine)
3099{
3100 /*
3101 * Lookup the VM so we can safely get the Machine instance.
3102 * (Don't want to test how reliable XPCOM and COM are with finding
3103 * the local object instance when a client passes a stub back.)
3104 */
3105 Bstr bstrUuidMachine;
3106 HRESULT hrc = aMachine->COMGETTER(Id)(bstrUuidMachine.asOutParam());
3107 if (SUCCEEDED(hrc))
3108 {
3109 Guid UuidMachine(bstrUuidMachine);
3110 ComObjPtr<Machine> ptrMachine;
3111 hrc = mParent->i_findMachine(UuidMachine, false /*fPermitInaccessible*/, true /*aSetError*/, &ptrMachine);
3112 if (SUCCEEDED(hrc))
3113 {
3114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3115 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER,
3116 tr("Cannot change after prepare() has been called")));
3117 mMachine = ptrMachine;
3118 mMachineUuid = UuidMachine;
3119 if (mfIsDefaultAuxiliaryBasePath)
3120 mStrAuxiliaryBasePath.setNull();
3121 hrc = S_OK;
3122 }
3123 }
3124 return hrc;
3125}
3126
3127HRESULT Unattended::getScriptTemplatePath(com::Utf8Str &aScriptTemplatePath)
3128{
3129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3130 if ( mStrScriptTemplatePath.isNotEmpty()
3131 || mpInstaller == NULL)
3132 aScriptTemplatePath = mStrScriptTemplatePath;
3133 else
3134 aScriptTemplatePath = mpInstaller->getTemplateFilePath();
3135 return S_OK;
3136}
3137
3138HRESULT Unattended::setScriptTemplatePath(const com::Utf8Str &aScriptTemplatePath)
3139{
3140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3141 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
3142 mStrScriptTemplatePath = aScriptTemplatePath;
3143 return S_OK;
3144}
3145
3146HRESULT Unattended::getPostInstallScriptTemplatePath(com::Utf8Str &aPostInstallScriptTemplatePath)
3147{
3148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3149 if ( mStrPostInstallScriptTemplatePath.isNotEmpty()
3150 || mpInstaller == NULL)
3151 aPostInstallScriptTemplatePath = mStrPostInstallScriptTemplatePath;
3152 else
3153 aPostInstallScriptTemplatePath = mpInstaller->getPostTemplateFilePath();
3154 return S_OK;
3155}
3156
3157HRESULT Unattended::setPostInstallScriptTemplatePath(const com::Utf8Str &aPostInstallScriptTemplatePath)
3158{
3159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3160 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
3161 mStrPostInstallScriptTemplatePath = aPostInstallScriptTemplatePath;
3162 return S_OK;
3163}
3164
3165HRESULT Unattended::getPostInstallCommand(com::Utf8Str &aPostInstallCommand)
3166{
3167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3168 aPostInstallCommand = mStrPostInstallCommand;
3169 return S_OK;
3170}
3171
3172HRESULT Unattended::setPostInstallCommand(const com::Utf8Str &aPostInstallCommand)
3173{
3174 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3175 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
3176 mStrPostInstallCommand = aPostInstallCommand;
3177 return S_OK;
3178}
3179
3180HRESULT Unattended::getExtraInstallKernelParameters(com::Utf8Str &aExtraInstallKernelParameters)
3181{
3182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3183 if ( mStrExtraInstallKernelParameters.isNotEmpty()
3184 || mpInstaller == NULL)
3185 aExtraInstallKernelParameters = mStrExtraInstallKernelParameters;
3186 else
3187 aExtraInstallKernelParameters = mpInstaller->getDefaultExtraInstallKernelParameters();
3188 return S_OK;
3189}
3190
3191HRESULT Unattended::setExtraInstallKernelParameters(const com::Utf8Str &aExtraInstallKernelParameters)
3192{
3193 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3194 AssertReturn(mpInstaller == NULL, setErrorBoth(E_FAIL, VERR_WRONG_ORDER, tr("Cannot change after prepare() has been called")));
3195 mStrExtraInstallKernelParameters = aExtraInstallKernelParameters;
3196 return S_OK;
3197}
3198
3199HRESULT Unattended::getDetectedOSTypeId(com::Utf8Str &aDetectedOSTypeId)
3200{
3201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3202 aDetectedOSTypeId = mStrDetectedOSTypeId;
3203 return S_OK;
3204}
3205
3206HRESULT Unattended::getDetectedOSVersion(com::Utf8Str &aDetectedOSVersion)
3207{
3208 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3209 aDetectedOSVersion = mStrDetectedOSVersion;
3210 return S_OK;
3211}
3212
3213HRESULT Unattended::getDetectedOSFlavor(com::Utf8Str &aDetectedOSFlavor)
3214{
3215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3216 aDetectedOSFlavor = mStrDetectedOSFlavor;
3217 return S_OK;
3218}
3219
3220HRESULT Unattended::getDetectedOSLanguages(com::Utf8Str &aDetectedOSLanguages)
3221{
3222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3223 aDetectedOSLanguages = RTCString::join(mDetectedOSLanguages, " ");
3224 return S_OK;
3225}
3226
3227HRESULT Unattended::getDetectedOSHints(com::Utf8Str &aDetectedOSHints)
3228{
3229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3230 aDetectedOSHints = mStrDetectedOSHints;
3231 return S_OK;
3232}
3233
3234HRESULT Unattended::getDetectedImageNames(std::vector<com::Utf8Str> &aDetectedImageNames)
3235{
3236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3237 aDetectedImageNames.clear();
3238 for (size_t i = 0; i < mDetectedImages.size(); ++i)
3239 aDetectedImageNames.push_back(mDetectedImages[i].getNameAndVersion());
3240 return S_OK;
3241}
3242
3243HRESULT Unattended::getDetectedImageIndices(std::vector<ULONG> &aDetectedImageIndices)
3244{
3245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3246 aDetectedImageIndices.clear();
3247 for (size_t i = 0; i < mDetectedImages.size(); ++i)
3248 aDetectedImageIndices.push_back(mDetectedImages[i].mImageIndex);
3249 return S_OK;
3250}
3251
3252/*
3253 * Getters that the installer and script classes can use.
3254 */
3255Utf8Str const &Unattended::i_getIsoPath() const
3256{
3257 Assert(isReadLockedOnCurrentThread());
3258 return mStrIsoPath;
3259}
3260
3261Utf8Str const &Unattended::i_getUser() const
3262{
3263 Assert(isReadLockedOnCurrentThread());
3264 return mStrUser;
3265}
3266
3267Utf8Str const &Unattended::i_getPassword() const
3268{
3269 Assert(isReadLockedOnCurrentThread());
3270 return mStrPassword;
3271}
3272
3273Utf8Str const &Unattended::i_getFullUserName() const
3274{
3275 Assert(isReadLockedOnCurrentThread());
3276 return mStrFullUserName.isNotEmpty() ? mStrFullUserName : mStrUser;
3277}
3278
3279Utf8Str const &Unattended::i_getProductKey() const
3280{
3281 Assert(isReadLockedOnCurrentThread());
3282 return mStrProductKey;
3283}
3284
3285Utf8Str const &Unattended::i_getProxy() const
3286{
3287 Assert(isReadLockedOnCurrentThread());
3288 return mStrProxy;
3289}
3290
3291Utf8Str const &Unattended::i_getAdditionsIsoPath() const
3292{
3293 Assert(isReadLockedOnCurrentThread());
3294 return mStrAdditionsIsoPath;
3295}
3296
3297bool Unattended::i_getInstallGuestAdditions() const
3298{
3299 Assert(isReadLockedOnCurrentThread());
3300 return mfInstallGuestAdditions;
3301}
3302
3303Utf8Str const &Unattended::i_getValidationKitIsoPath() const
3304{
3305 Assert(isReadLockedOnCurrentThread());
3306 return mStrValidationKitIsoPath;
3307}
3308
3309bool Unattended::i_getInstallTestExecService() const
3310{
3311 Assert(isReadLockedOnCurrentThread());
3312 return mfInstallTestExecService;
3313}
3314
3315Utf8Str const &Unattended::i_getTimeZone() const
3316{
3317 Assert(isReadLockedOnCurrentThread());
3318 return mStrTimeZone;
3319}
3320
3321PCRTTIMEZONEINFO Unattended::i_getTimeZoneInfo() const
3322{
3323 Assert(isReadLockedOnCurrentThread());
3324 return mpTimeZoneInfo;
3325}
3326
3327Utf8Str const &Unattended::i_getLocale() const
3328{
3329 Assert(isReadLockedOnCurrentThread());
3330 return mStrLocale;
3331}
3332
3333Utf8Str const &Unattended::i_getLanguage() const
3334{
3335 Assert(isReadLockedOnCurrentThread());
3336 return mStrLanguage;
3337}
3338
3339Utf8Str const &Unattended::i_getCountry() const
3340{
3341 Assert(isReadLockedOnCurrentThread());
3342 return mStrCountry;
3343}
3344
3345bool Unattended::i_isMinimalInstallation() const
3346{
3347 size_t i = mPackageSelectionAdjustments.size();
3348 while (i-- > 0)
3349 if (mPackageSelectionAdjustments[i].equals("minimal"))
3350 return true;
3351 return false;
3352}
3353
3354Utf8Str const &Unattended::i_getHostname() const
3355{
3356 Assert(isReadLockedOnCurrentThread());
3357 return mStrHostname;
3358}
3359
3360Utf8Str const &Unattended::i_getAuxiliaryBasePath() const
3361{
3362 Assert(isReadLockedOnCurrentThread());
3363 return mStrAuxiliaryBasePath;
3364}
3365
3366ULONG Unattended::i_getImageIndex() const
3367{
3368 Assert(isReadLockedOnCurrentThread());
3369 return midxImage;
3370}
3371
3372Utf8Str const &Unattended::i_getScriptTemplatePath() const
3373{
3374 Assert(isReadLockedOnCurrentThread());
3375 return mStrScriptTemplatePath;
3376}
3377
3378Utf8Str const &Unattended::i_getPostInstallScriptTemplatePath() const
3379{
3380 Assert(isReadLockedOnCurrentThread());
3381 return mStrPostInstallScriptTemplatePath;
3382}
3383
3384Utf8Str const &Unattended::i_getPostInstallCommand() const
3385{
3386 Assert(isReadLockedOnCurrentThread());
3387 return mStrPostInstallCommand;
3388}
3389
3390Utf8Str const &Unattended::i_getAuxiliaryInstallDir() const
3391{
3392 Assert(isReadLockedOnCurrentThread());
3393 /* Only the installer knows, forward the call. */
3394 AssertReturn(mpInstaller != NULL, Utf8Str::Empty);
3395 return mpInstaller->getAuxiliaryInstallDir();
3396}
3397
3398Utf8Str const &Unattended::i_getExtraInstallKernelParameters() const
3399{
3400 Assert(isReadLockedOnCurrentThread());
3401 return mStrExtraInstallKernelParameters;
3402}
3403
3404bool Unattended::i_isRtcUsingUtc() const
3405{
3406 Assert(isReadLockedOnCurrentThread());
3407 return mfRtcUseUtc;
3408}
3409
3410bool Unattended::i_isGuestOs64Bit() const
3411{
3412 Assert(isReadLockedOnCurrentThread());
3413 return mfGuestOs64Bit;
3414}
3415
3416bool Unattended::i_isFirmwareEFI() const
3417{
3418 Assert(isReadLockedOnCurrentThread());
3419 return menmFirmwareType != FirmwareType_BIOS;
3420}
3421
3422Utf8Str const &Unattended::i_getDetectedOSVersion()
3423{
3424 Assert(isReadLockedOnCurrentThread());
3425 return mStrDetectedOSVersion;
3426}
3427
3428HRESULT Unattended::i_attachImage(UnattendedInstallationDisk const *pImage, ComPtr<IMachine> const &rPtrSessionMachine,
3429 AutoMultiWriteLock2 &rLock)
3430{
3431 /*
3432 * Attach the disk image
3433 * HACK ALERT! Temporarily release the Unattended lock.
3434 */
3435 rLock.release();
3436
3437 ComPtr<IMedium> ptrMedium;
3438 HRESULT rc = mParent->OpenMedium(Bstr(pImage->strImagePath).raw(),
3439 pImage->enmDeviceType,
3440 pImage->enmAccessType,
3441 true,
3442 ptrMedium.asOutParam());
3443 LogRelFlowFunc(("VirtualBox::openMedium -> %Rhrc\n", rc));
3444 if (SUCCEEDED(rc))
3445 {
3446 if (pImage->fMountOnly)
3447 {
3448 // mount the opened disk image
3449 rc = rPtrSessionMachine->MountMedium(Bstr(pImage->strControllerName).raw(), pImage->iPort,
3450 pImage->iDevice, ptrMedium, TRUE /*fForce*/);
3451 LogRelFlowFunc(("Machine::MountMedium -> %Rhrc\n", rc));
3452 }
3453 else
3454 {
3455 //attach the opened disk image to the controller
3456 rc = rPtrSessionMachine->AttachDevice(Bstr(pImage->strControllerName).raw(), pImage->iPort,
3457 pImage->iDevice, pImage->enmDeviceType, ptrMedium);
3458 LogRelFlowFunc(("Machine::AttachDevice -> %Rhrc\n", rc));
3459 }
3460 }
3461
3462 rLock.acquire();
3463 return rc;
3464}
3465
3466bool Unattended::i_isGuestOSArchX64(Utf8Str const &rStrGuestOsTypeId)
3467{
3468 ComPtr<IGuestOSType> pGuestOSType;
3469 HRESULT hrc = mParent->GetGuestOSType(Bstr(rStrGuestOsTypeId).raw(), pGuestOSType.asOutParam());
3470 if (SUCCEEDED(hrc))
3471 {
3472 BOOL fIs64Bit = FALSE;
3473 if (!pGuestOSType.isNull())
3474 hrc = pGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit);
3475 if (SUCCEEDED(hrc))
3476 return fIs64Bit != FALSE;
3477 }
3478 return false;
3479}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette