VirtualBox

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

Last change on this file since 79812 was 79417, checked in by vboxsync, 5 years ago

Main/UnattendedImpl: Implemented detecting ubuntu ISOs. bugref:9151

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