VirtualBox

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

Last change on this file since 69731 was 69238, checked in by vboxsync, 7 years ago

Main: scm updates

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