VirtualBox

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

Last change on this file since 92154 was 91718, checked in by vboxsync, 3 years ago

Main: bugref:1909: Added initial translation to Russian of API messages. Fixed errors and plurals wherever needed. Fixed type of the plural argument in the tr()

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

© 2023 Oracle
ContactPrivacy policyTerms of Use