VirtualBox

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

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

Main/Unattended: Use the detected guest OS type instead of the configured VM guest OS type for picking the installer. bugref:10186 bugref:9515

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

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