VirtualBox

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

Last change on this file since 85672 was 85277, checked in by vboxsync, 4 years ago

Main/UnattendedImpl.cpp: Signed/unsigned conversion issues wrt Port and Device numbers. Limits are ULONG whereas all other arguments are LONG. bugref:9790

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