VirtualBox

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

Last change on this file since 74897 was 73505, checked in by vboxsync, 6 years ago

Main,Config.kmk: GCC 8.2.0 fixes

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