VirtualBox

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

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

Main/Unattended: bugref:9781. Deleting exra new lines.

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

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