VirtualBox

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

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

Main/UnattendedImpl: ​​bugref:10182. Stop OS detection if we fail to determine the OS architecture.

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

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