VirtualBox

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

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

Main/Unattended: Correcting a typo.

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

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