VirtualBox

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

Last change on this file since 79241 was 78967, checked in by vboxsync, 6 years ago

Main,GUI: S_FALSE is defined by com/defs.h now.

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

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