VirtualBox

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

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

Main/Unattended: Forgotten member update to match VM settings. Caused treating all unattended installs as EFI.

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

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