VirtualBox

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

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

Main: Generate enum value to string conversion functions for the API. Use these for logging instead of the Global::stringify* ones as they are untranslated, the Global:: ones are for use in error message when translated enum value names are desired (questionable).

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

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