VirtualBox

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

Last change on this file since 81916 was 79931, checked in by vboxsync, 5 years ago

Main/Unattended: Recognize vista isos. bugref:9151

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

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