VirtualBox

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

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

Main/Unattended: Move mStrProxy to the right group and added @todos about severly insufficient setter implementation. bugref:9469

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

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