VirtualBox

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

Last change on this file since 69063 was 69063, checked in by vboxsync, 8 years ago

Main/Unattended: Get installer languages from sources/lang.ini. We've got a UDF reader now.

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

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