VirtualBox

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

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

Main/Unattended: The image version is inside the WINDOWS element, not directly under IMAGE. Pick up SPBUILD and combine them all into a single version string. Be stricter with the image index parsing (via getAttributeValue override). Catch alloc exceptions. bugref:9781

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

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