VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/UnattendedInstaller.cpp@ 96172

最後變更 在這個檔案從96172是 95436,由 vboxsync 提交於 3 年 前

Main/UnattendedInstall: Minimalistic support for FreeBSD >= 10.0 sitting in my tree for too long, bugref:9781

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 56.8 KB
 
1/* $Id: UnattendedInstaller.cpp 95436 2022-06-29 18:18:57Z vboxsync $ */
2/** @file
3 * UnattendedInstaller class and it's descendants implementation
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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 "VirtualBoxErrorInfoImpl.h"
26#include "AutoCaller.h"
27#include <VBox/com/ErrorInfo.h>
28
29#include "UnattendedImpl.h"
30#include "UnattendedInstaller.h"
31#include "UnattendedScript.h"
32
33#include <VBox/err.h>
34#include <iprt/ctype.h>
35#include <iprt/fsisomaker.h>
36#include <iprt/fsvfs.h>
37#include <iprt/getopt.h>
38#include <iprt/file.h>
39#include <iprt/path.h>
40#include <iprt/stream.h>
41#include <iprt/vfs.h>
42#ifdef RT_OS_SOLARIS
43# undef ES /* Workaround for someone dragging the namespace pollutor sys/regset.h. Sigh. */
44#endif
45#include <iprt/formats/iso9660.h>
46#include <iprt/cpp/path.h>
47
48
49using namespace std;
50
51
52/* static */ UnattendedInstaller *
53UnattendedInstaller::createInstance(VBOXOSTYPE enmDetectedOSType, const Utf8Str &strDetectedOSType,
54 const Utf8Str &strDetectedOSVersion, const Utf8Str &strDetectedOSFlavor,
55 const Utf8Str &strDetectedOSHints, Unattended *pParent)
56{
57 UnattendedInstaller *pUinstaller = NULL;
58
59 if (strDetectedOSType.find("Windows") != RTCString::npos)
60 {
61 if (enmDetectedOSType >= VBOXOSTYPE_WinVista)
62 pUinstaller = new UnattendedWindowsXmlInstaller(pParent);
63 else
64 pUinstaller = new UnattendedWindowsSifInstaller(pParent);
65 }
66 else if (enmDetectedOSType >= VBOXOSTYPE_OS2 && enmDetectedOSType < VBOXOSTYPE_Linux)
67 pUinstaller = new UnattendedOs2Installer(pParent, strDetectedOSHints);
68 else
69 {
70 if (enmDetectedOSType >= VBOXOSTYPE_Debian && enmDetectedOSType <= VBOXOSTYPE_Debian_latest_x64)
71 pUinstaller = new UnattendedDebianInstaller(pParent);
72 else if (enmDetectedOSType >= VBOXOSTYPE_Ubuntu && enmDetectedOSType <= VBOXOSTYPE_Ubuntu_latest_x64)
73 pUinstaller = new UnattendedUbuntuInstaller(pParent);
74 else if (enmDetectedOSType >= VBOXOSTYPE_RedHat && enmDetectedOSType <= VBOXOSTYPE_RedHat_latest_x64)
75 {
76 if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "8") >= 0)
77 pUinstaller = new UnattendedRhel8Installer(pParent);
78 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "7") >= 0)
79 pUinstaller = new UnattendedRhel7Installer(pParent);
80 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "6") >= 0)
81 pUinstaller = new UnattendedRhel6Installer(pParent);
82 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "5") >= 0)
83 pUinstaller = new UnattendedRhel5Installer(pParent);
84 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "4") >= 0)
85 pUinstaller = new UnattendedRhel4Installer(pParent);
86 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "3") >= 0)
87 pUinstaller = new UnattendedRhel3Installer(pParent);
88 else
89 pUinstaller = new UnattendedRhel6Installer(pParent);
90 }
91 else if (enmDetectedOSType >= VBOXOSTYPE_FedoraCore && enmDetectedOSType <= VBOXOSTYPE_FedoraCore_x64)
92 pUinstaller = new UnattendedFedoraInstaller(pParent);
93 else if (enmDetectedOSType >= VBOXOSTYPE_Oracle && enmDetectedOSType <= VBOXOSTYPE_Oracle_latest_x64)
94 {
95 if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "8") >= 0)
96 pUinstaller = new UnattendedOracleLinux8Installer(pParent);
97 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "7") >= 0)
98 pUinstaller = new UnattendedOracleLinux7Installer(pParent);
99 else if (RTStrVersionCompare(strDetectedOSVersion.c_str(), "6") >= 0)
100 pUinstaller = new UnattendedOracleLinux6Installer(pParent);
101 else
102 pUinstaller = new UnattendedOracleLinux6Installer(pParent);
103 }
104 else if (enmDetectedOSType >= VBOXOSTYPE_FreeBSD && enmDetectedOSType <= VBOXOSTYPE_FreeBSD_x64)
105 pUinstaller = new UnattendedFreeBsdInstaller(pParent);
106#if 0 /* doesn't work, so convert later. */
107 else if (enmDetectedOSType == VBOXOSTYPE_OpenSUSE || enmDetectedOSType == VBOXOSTYPE_OpenSUSE_x64)
108 pUinstaller = new UnattendedSuseInstaller(new UnattendedSUSEXMLScript(pParent), pParent);
109#endif
110 }
111 RT_NOREF_PV(strDetectedOSFlavor);
112 RT_NOREF_PV(strDetectedOSHints);
113 return pUinstaller;
114}
115
116
117//////////////////////////////////////////////////////////////////////////////////////////////////////
118/*
119*
120*
121* Implementation Unattended functions
122*
123*/
124//////////////////////////////////////////////////////////////////////////////////////////////////////
125
126/*
127 *
128 * UnattendedInstaller public methods
129 *
130 */
131UnattendedInstaller::UnattendedInstaller(Unattended *pParent,
132 const char *pszMainScriptTemplateName, const char *pszPostScriptTemplateName,
133 const char *pszMainScriptFilename, const char *pszPostScriptFilename,
134 DeviceType_T enmBootDevice /*= DeviceType_DVD */)
135 : mMainScript(pParent, pszMainScriptTemplateName, pszMainScriptFilename)
136 , mPostScript(pParent, pszPostScriptTemplateName, pszPostScriptFilename)
137 , mpParent(pParent)
138 , meBootDevice(enmBootDevice)
139{
140 AssertPtr(pParent);
141 Assert(*pszMainScriptTemplateName);
142 Assert(*pszMainScriptFilename);
143 Assert(*pszPostScriptTemplateName);
144 Assert(*pszPostScriptFilename);
145 Assert(enmBootDevice == DeviceType_DVD || enmBootDevice == DeviceType_Floppy);
146}
147
148UnattendedInstaller::~UnattendedInstaller()
149{
150 mpParent = NULL;
151}
152
153HRESULT UnattendedInstaller::initInstaller()
154{
155 /*
156 * Calculate the full main script template location.
157 */
158 if (mpParent->i_getScriptTemplatePath().isNotEmpty())
159 mStrMainScriptTemplate = mpParent->i_getScriptTemplatePath();
160 else
161 {
162 int vrc = RTPathAppPrivateNoArchCxx(mStrMainScriptTemplate);
163 if (RT_SUCCESS(vrc))
164 vrc = RTPathAppendCxx(mStrMainScriptTemplate, "UnattendedTemplates");
165 if (RT_SUCCESS(vrc))
166 vrc = RTPathAppendCxx(mStrMainScriptTemplate, mMainScript.getDefaultTemplateFilename());
167 if (RT_FAILURE(vrc))
168 return mpParent->setErrorBoth(E_FAIL, vrc,
169 tr("Failed to construct path to the unattended installer script templates (%Rrc)"),
170 vrc);
171 }
172
173 /*
174 * Calculate the full post script template location.
175 */
176 if (mpParent->i_getPostInstallScriptTemplatePath().isNotEmpty())
177 mStrPostScriptTemplate = mpParent->i_getPostInstallScriptTemplatePath();
178 else
179 {
180 int vrc = RTPathAppPrivateNoArchCxx(mStrPostScriptTemplate);
181 if (RT_SUCCESS(vrc))
182 vrc = RTPathAppendCxx(mStrPostScriptTemplate, "UnattendedTemplates");
183 if (RT_SUCCESS(vrc))
184 vrc = RTPathAppendCxx(mStrPostScriptTemplate, mPostScript.getDefaultTemplateFilename());
185 if (RT_FAILURE(vrc))
186 return mpParent->setErrorBoth(E_FAIL, vrc,
187 tr("Failed to construct path to the unattended installer script templates (%Rrc)"),
188 vrc);
189 }
190
191 /*
192 * Construct paths we need.
193 */
194 if (isAuxiliaryFloppyNeeded())
195 {
196 mStrAuxiliaryFloppyFilePath = mpParent->i_getAuxiliaryBasePath();
197 mStrAuxiliaryFloppyFilePath.append("aux-floppy.img");
198 }
199 if (isAuxiliaryIsoNeeded())
200 {
201 mStrAuxiliaryIsoFilePath = mpParent->i_getAuxiliaryBasePath();
202 if (!isAuxiliaryIsoIsVISO())
203 mStrAuxiliaryIsoFilePath.append("aux-iso.iso");
204 else
205 mStrAuxiliaryIsoFilePath.append("aux-iso.viso");
206 }
207
208 /*
209 * Check that we've got the minimum of data available.
210 */
211 if (mpParent->i_getIsoPath().isEmpty())
212 return mpParent->setError(E_INVALIDARG, tr("Cannot proceed with an empty installation ISO path"));
213 if (mpParent->i_getUser().isEmpty())
214 return mpParent->setError(E_INVALIDARG, tr("Empty user name is not allowed"));
215 if (mpParent->i_getPassword().isEmpty())
216 return mpParent->setError(E_INVALIDARG, tr("Empty password is not allowed"));
217
218 LogRelFunc(("UnattendedInstaller::savePassedData(): \n"));
219 return S_OK;
220}
221
222#if 0 /* Always in AUX ISO */
223bool UnattendedInstaller::isAdditionsIsoNeeded() const
224{
225 /* In the VISO case, we'll add the additions to the VISO in a subdir. */
226 return !isAuxiliaryIsoIsVISO() && mpParent->i_getInstallGuestAdditions();
227}
228
229bool UnattendedInstaller::isValidationKitIsoNeeded() const
230{
231 /* In the VISO case, we'll add the validation kit to the VISO in a subdir. */
232 return !isAuxiliaryIsoIsVISO() && mpParent->i_getInstallTestExecService();
233}
234#endif
235
236bool UnattendedInstaller::isAuxiliaryIsoNeeded() const
237{
238 /* In the VISO case we use the AUX ISO for GAs and TXS. */
239 return isAuxiliaryIsoIsVISO()
240 && ( mpParent->i_getInstallGuestAdditions()
241 || mpParent->i_getInstallTestExecService());
242}
243
244
245HRESULT UnattendedInstaller::prepareUnattendedScripts()
246{
247 LogFlow(("UnattendedInstaller::prepareUnattendedScripts()\n"));
248
249 /*
250 * The script template editor calls setError, so status codes just needs to
251 * be passed on to the caller. Do the same for both scripts.
252 */
253 HRESULT hrc = mMainScript.read(getTemplateFilePath());
254 if (SUCCEEDED(hrc))
255 {
256 hrc = mMainScript.parse();
257 if (SUCCEEDED(hrc))
258 {
259 /* Ditto for the post script. */
260 hrc = mPostScript.read(getPostTemplateFilePath());
261 if (SUCCEEDED(hrc))
262 {
263 hrc = mPostScript.parse();
264 if (SUCCEEDED(hrc))
265 {
266 LogFlow(("UnattendedInstaller::prepareUnattendedScripts: returns S_OK\n"));
267 return S_OK;
268 }
269 LogFlow(("UnattendedInstaller::prepareUnattendedScripts: parse failed on post script (%Rhrc)\n", hrc));
270 }
271 else
272 LogFlow(("UnattendedInstaller::prepareUnattendedScripts: error reading post install script template file (%Rhrc)\n", hrc));
273 }
274 else
275 LogFlow(("UnattendedInstaller::prepareUnattendedScripts: parse failed (%Rhrc)\n", hrc));
276 }
277 else
278 LogFlow(("UnattendedInstaller::prepareUnattendedScripts: error reading installation script template file (%Rhrc)\n", hrc));
279 return hrc;
280}
281
282HRESULT UnattendedInstaller::prepareMedia(bool fOverwrite /*=true*/)
283{
284 LogRelFlow(("UnattendedInstaller::prepareMedia:\n"));
285 HRESULT hrc = S_OK;
286 if (isAuxiliaryFloppyNeeded())
287 hrc = prepareAuxFloppyImage(fOverwrite);
288 if (SUCCEEDED(hrc))
289 {
290 if (isAuxiliaryIsoNeeded())
291 {
292 hrc = prepareAuxIsoImage(fOverwrite);
293 if (FAILED(hrc))
294 {
295 LogRelFlow(("UnattendedInstaller::prepareMedia: prepareAuxIsoImage failed\n"));
296
297 /* Delete the floppy image if we created one. */
298 if (isAuxiliaryFloppyNeeded())
299 RTFileDelete(getAuxiliaryFloppyFilePath().c_str());
300 }
301 }
302 }
303 LogRelFlow(("UnattendedInstaller::prepareMedia: returns %Rrc\n", hrc));
304 return hrc;
305}
306
307/*
308 *
309 * UnattendedInstaller protected methods
310 *
311 */
312HRESULT UnattendedInstaller::prepareAuxFloppyImage(bool fOverwrite)
313{
314 Assert(isAuxiliaryFloppyNeeded());
315
316 /*
317 * Create the image.
318 */
319 RTVFSFILE hVfsFile;
320 HRESULT hrc = newAuxFloppyImage(getAuxiliaryFloppyFilePath().c_str(), fOverwrite, &hVfsFile);
321 if (SUCCEEDED(hrc))
322 {
323 /*
324 * Open the FAT file system so we can copy files onto the floppy.
325 */
326 RTERRINFOSTATIC ErrInfo;
327 RTVFS hVfs;
328 int vrc = RTFsFatVolOpen(hVfsFile, false /*fReadOnly*/, 0 /*offBootSector*/, &hVfs, RTErrInfoInitStatic(&ErrInfo));
329 RTVfsFileRelease(hVfsFile);
330 if (RT_SUCCESS(vrc))
331 {
332 /*
333 * Call overridable method to copies the files onto it.
334 */
335 hrc = copyFilesToAuxFloppyImage(hVfs);
336
337 /*
338 * Release the VFS. On failure, delete the floppy image so the operation can
339 * be repeated in non-overwrite mode and so that we don't leave any mess behind.
340 */
341 RTVfsRelease(hVfs);
342 }
343 else if (RTErrInfoIsSet(&ErrInfo.Core))
344 hrc = mpParent->setErrorBoth(E_FAIL, vrc,
345 tr("Failed to open FAT file system on newly created floppy image '%s': %Rrc: %s"),
346 getAuxiliaryFloppyFilePath().c_str(), vrc, ErrInfo.Core.pszMsg);
347 else
348 hrc = mpParent->setErrorBoth(E_FAIL, vrc,
349 tr("Failed to open FAT file system onnewly created floppy image '%s': %Rrc"),
350 getAuxiliaryFloppyFilePath().c_str(), vrc);
351 if (FAILED(hrc))
352 RTFileDelete(getAuxiliaryFloppyFilePath().c_str());
353 }
354 return hrc;
355}
356
357HRESULT UnattendedInstaller::newAuxFloppyImage(const char *pszFilename, bool fOverwrite, PRTVFSFILE phVfsFile)
358{
359 /*
360 * Open the image file.
361 */
362 HRESULT hrc;
363 RTVFSFILE hVfsFile;
364 uint64_t fOpen = RTFILE_O_READWRITE | RTFILE_O_DENY_ALL | (0660 << RTFILE_O_CREATE_MODE_SHIFT);
365 if (fOverwrite)
366 fOpen |= RTFILE_O_CREATE_REPLACE;
367 else
368 fOpen |= RTFILE_O_OPEN;
369 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
370 if (RT_SUCCESS(vrc))
371 {
372 /*
373 * Format it.
374 */
375 vrc = RTFsFatVolFormat144(hVfsFile, false /*fQuick*/);
376 if (RT_SUCCESS(vrc))
377 {
378 *phVfsFile = hVfsFile;
379 LogRelFlow(("UnattendedInstaller::newAuxFloppyImage: created and formatted '%s'\n", pszFilename));
380 return S_OK;
381 }
382
383 hrc = mpParent->setErrorBoth(E_FAIL, vrc, tr("Failed to format floppy image '%s': %Rrc"), pszFilename, vrc);
384 RTVfsFileRelease(hVfsFile);
385 RTFileDelete(pszFilename);
386 }
387 else
388 hrc = mpParent->setErrorBoth(E_FAIL, vrc, tr("Failed to create floppy image '%s': %Rrc"), pszFilename, vrc);
389 return hrc;
390}
391
392HRESULT UnattendedInstaller::copyFilesToAuxFloppyImage(RTVFS hVfs)
393{
394 HRESULT hrc = addScriptToFloppyImage(&mMainScript, hVfs);
395 if (SUCCEEDED(hrc))
396 hrc = addScriptToFloppyImage(&mPostScript, hVfs);
397 return hrc;
398}
399
400HRESULT UnattendedInstaller::addScriptToFloppyImage(BaseTextScript *pEditor, RTVFS hVfs)
401{
402 /*
403 * Open the destination file.
404 */
405 HRESULT hrc;
406 RTVFSFILE hVfsFileDst;
407 int vrc = RTVfsFileOpen(hVfs, pEditor->getDefaultFilename(),
408 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_ALL
409 | (0660 << RTFILE_O_CREATE_MODE_SHIFT),
410 &hVfsFileDst);
411 if (RT_SUCCESS(vrc))
412 {
413 /*
414 * Save the content to a string.
415 */
416 Utf8Str strScript;
417 hrc = pEditor->saveToString(strScript);
418 if (SUCCEEDED(hrc))
419 {
420 /*
421 * Write the string.
422 */
423 vrc = RTVfsFileWrite(hVfsFileDst, strScript.c_str(), strScript.length(), NULL);
424 if (RT_SUCCESS(vrc))
425 hrc = S_OK; /* done */
426 else
427 hrc = mpParent->setErrorBoth(E_FAIL, vrc,
428 tr("Error writing %zu bytes to '%s' in floppy image '%s': %Rrc",
429 "", strScript.length()),
430 strScript.length(), pEditor->getDefaultFilename(),
431 getAuxiliaryFloppyFilePath().c_str());
432 }
433 RTVfsFileRelease(hVfsFileDst);
434 }
435 else
436 hrc = mpParent->setErrorBoth(E_FAIL, vrc,
437 tr("Error creating '%s' in floppy image '%s': %Rrc"),
438 pEditor->getDefaultFilename(), getAuxiliaryFloppyFilePath().c_str());
439 return hrc;
440}
441
442HRESULT UnattendedInstaller::addFileToFloppyImage(RTVFS hVfs, const char *pszSrc, const char *pszDst)
443{
444 HRESULT hrc;
445
446 /*
447 * Open the source file.
448 */
449 RTVFSIOSTREAM hVfsIosSrc;
450 int vrc = RTVfsIoStrmOpenNormal(pszSrc, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsIosSrc);
451 if (RT_SUCCESS(vrc))
452 {
453 /*
454 * Open the destination file.
455 */
456 RTVFSFILE hVfsFileDst;
457 vrc = RTVfsFileOpen(hVfs, pszDst,
458 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_ALL | (0660 << RTFILE_O_CREATE_MODE_SHIFT),
459 &hVfsFileDst);
460 if (RT_SUCCESS(vrc))
461 {
462 /*
463 * Do the copying.
464 */
465 RTVFSIOSTREAM hVfsIosDst = RTVfsFileToIoStream(hVfsFileDst);
466 vrc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, 0);
467 if (RT_SUCCESS(vrc))
468 hrc = S_OK;
469 else
470 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error writing copying '%s' to floppy image '%s': %Rrc"),
471 pszSrc, getAuxiliaryFloppyFilePath().c_str(), vrc);
472 RTVfsIoStrmRelease(hVfsIosDst);
473 RTVfsFileRelease(hVfsFileDst);
474 }
475 else
476 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error opening '%s' on floppy image '%s' for writing: %Rrc"),
477 pszDst, getAuxiliaryFloppyFilePath().c_str(), vrc);
478
479 RTVfsIoStrmRelease(hVfsIosSrc);
480 }
481 else
482 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error opening '%s' for copying onto floppy image '%s': %Rrc"),
483 pszSrc, getAuxiliaryFloppyFilePath().c_str(), vrc);
484 return hrc;
485}
486
487HRESULT UnattendedInstaller::prepareAuxIsoImage(bool fOverwrite)
488{
489 /*
490 * Open the original installation ISO.
491 */
492 RTVFS hVfsOrgIso;
493 HRESULT hrc = openInstallIsoImage(&hVfsOrgIso);
494 if (SUCCEEDED(hrc))
495 {
496 /*
497 * The next steps depends on the kind of image we're making.
498 */
499 if (!isAuxiliaryIsoIsVISO())
500 {
501 RTFSISOMAKER hIsoMaker;
502 hrc = newAuxIsoImageMaker(&hIsoMaker);
503 if (SUCCEEDED(hrc))
504 {
505 hrc = addFilesToAuxIsoImageMaker(hIsoMaker, hVfsOrgIso);
506 if (SUCCEEDED(hrc))
507 hrc = finalizeAuxIsoImage(hIsoMaker, getAuxiliaryIsoFilePath().c_str(), fOverwrite);
508 RTFsIsoMakerRelease(hIsoMaker);
509 }
510 }
511 else
512 {
513 RTCList<RTCString> vecFiles(0);
514 RTCList<RTCString> vecArgs(0);
515 try
516 {
517 vecArgs.append() = "--iprt-iso-maker-file-marker-bourne-sh";
518 RTUUID Uuid;
519 int vrc = RTUuidCreate(&Uuid); AssertRC(vrc);
520 char szTmp[RTUUID_STR_LENGTH + 1];
521 vrc = RTUuidToStr(&Uuid, szTmp, sizeof(szTmp)); AssertRC(vrc);
522 vecArgs.append() = szTmp;
523 vecArgs.append() = "--file-mode=0444";
524 vecArgs.append() = "--dir-mode=0555";
525 }
526 catch (std::bad_alloc &)
527 {
528 hrc = E_OUTOFMEMORY;
529 }
530 if (SUCCEEDED(hrc))
531 {
532 hrc = addFilesToAuxVisoVectors(vecArgs, vecFiles, hVfsOrgIso, fOverwrite);
533 if (SUCCEEDED(hrc))
534 hrc = finalizeAuxVisoFile(vecArgs, getAuxiliaryIsoFilePath().c_str(), fOverwrite);
535
536 if (FAILED(hrc))
537 for (size_t i = 0; i < vecFiles.size(); i++)
538 RTFileDelete(vecFiles[i].c_str());
539 }
540 }
541 RTVfsRelease(hVfsOrgIso);
542 }
543 return hrc;
544}
545
546HRESULT UnattendedInstaller::openInstallIsoImage(PRTVFS phVfsIso, uint32_t fFlags /*= 0*/)
547{
548 /* Open the file. */
549 const char *pszIsoPath = mpParent->i_getIsoPath().c_str();
550 RTVFSFILE hOrgIsoFile;
551 int vrc = RTVfsFileOpenNormal(pszIsoPath, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hOrgIsoFile);
552 if (RT_FAILURE(vrc))
553 return mpParent->setErrorBoth(E_FAIL, vrc, tr("Failed to open ISO image '%s' (%Rrc)"), pszIsoPath, vrc);
554
555 /* Pass the file to the ISO file system interpreter. */
556 RTERRINFOSTATIC ErrInfo;
557 vrc = RTFsIso9660VolOpen(hOrgIsoFile, fFlags, phVfsIso, RTErrInfoInitStatic(&ErrInfo));
558 RTVfsFileRelease(hOrgIsoFile);
559 if (RT_SUCCESS(vrc))
560 return S_OK;
561 if (RTErrInfoIsSet(&ErrInfo.Core))
562 return mpParent->setErrorBoth(E_FAIL, vrc, tr("ISO reader fail to open '%s' (%Rrc): %s"),
563 pszIsoPath, vrc, ErrInfo.Core.pszMsg);
564 return mpParent->setErrorBoth(E_FAIL, vrc, tr("ISO reader fail to open '%s' (%Rrc)"), pszIsoPath, vrc);
565}
566
567HRESULT UnattendedInstaller::newAuxIsoImageMaker(PRTFSISOMAKER phIsoMaker)
568{
569 int vrc = RTFsIsoMakerCreate(phIsoMaker);
570 if (RT_SUCCESS(vrc))
571 return S_OK;
572 return mpParent->setErrorBoth(E_FAIL, vrc, tr("RTFsIsoMakerCreate failed (%Rrc)"), vrc);
573}
574
575HRESULT UnattendedInstaller::addFilesToAuxIsoImageMaker(RTFSISOMAKER hIsoMaker, RTVFS hVfsOrgIso)
576{
577 RT_NOREF(hVfsOrgIso);
578
579 /*
580 * Add the two scripts to the image with default names.
581 */
582 HRESULT hrc = addScriptToIsoMaker(&mMainScript, hIsoMaker);
583 if (SUCCEEDED(hrc))
584 hrc = addScriptToIsoMaker(&mPostScript, hIsoMaker);
585 return hrc;
586}
587
588HRESULT UnattendedInstaller::addScriptToIsoMaker(BaseTextScript *pEditor, RTFSISOMAKER hIsoMaker,
589 const char *pszDstFilename /*= NULL*/)
590{
591 /*
592 * Calc default destination filename if desired.
593 */
594 RTCString strDstNameBuf;
595 if (!pszDstFilename)
596 {
597 try
598 {
599 strDstNameBuf = RTPATH_SLASH_STR;
600 strDstNameBuf.append(pEditor->getDefaultTemplateFilename());
601 pszDstFilename = strDstNameBuf.c_str();
602 }
603 catch (std::bad_alloc &)
604 {
605 return E_OUTOFMEMORY;
606 }
607 }
608
609 /*
610 * Create a memory file for the script.
611 */
612 Utf8Str strScript;
613 HRESULT hrc = pEditor->saveToString(strScript);
614 if (SUCCEEDED(hrc))
615 {
616 RTVFSFILE hVfsScriptFile;
617 size_t cchScript = strScript.length();
618 int vrc = RTVfsFileFromBuffer(RTFILE_O_READ, strScript.c_str(), strScript.length(), &hVfsScriptFile);
619 strScript.setNull();
620 if (RT_SUCCESS(vrc))
621 {
622 /*
623 * Add it to the ISO.
624 */
625 vrc = RTFsIsoMakerAddFileWithVfsFile(hIsoMaker, pszDstFilename, hVfsScriptFile, NULL);
626 RTVfsFileRelease(hVfsScriptFile);
627 if (RT_SUCCESS(vrc))
628 hrc = S_OK;
629 else
630 hrc = mpParent->setErrorBoth(E_FAIL, vrc,
631 tr("RTFsIsoMakerAddFileWithVfsFile failed on the script '%s' (%Rrc)"),
632 pszDstFilename, vrc);
633 }
634 else
635 hrc = mpParent->setErrorBoth(E_FAIL, vrc,
636 tr("RTVfsFileFromBuffer failed on the %zu byte script '%s' (%Rrc)", "", cchScript),
637 cchScript, pszDstFilename, vrc);
638 }
639 return hrc;
640}
641
642HRESULT UnattendedInstaller::finalizeAuxIsoImage(RTFSISOMAKER hIsoMaker, const char *pszFilename, bool fOverwrite)
643{
644 /*
645 * Finalize the image.
646 */
647 int vrc = RTFsIsoMakerFinalize(hIsoMaker);
648 if (RT_FAILURE(vrc))
649 return mpParent->setErrorBoth(E_FAIL, vrc, tr("RTFsIsoMakerFinalize failed (%Rrc)"), vrc);
650
651 /*
652 * Open the destination file.
653 */
654 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_ALL;
655 if (fOverwrite)
656 fOpen |= RTFILE_O_CREATE_REPLACE;
657 else
658 fOpen |= RTFILE_O_CREATE;
659 RTVFSFILE hVfsDstFile;
660 vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsDstFile);
661 if (RT_FAILURE(vrc))
662 {
663 if (vrc == VERR_ALREADY_EXISTS)
664 return mpParent->setErrorBoth(E_FAIL, vrc, tr("The auxiliary ISO image file '%s' already exists"),
665 pszFilename);
666 return mpParent->setErrorBoth(E_FAIL, vrc, tr("Failed to open the auxiliary ISO image file '%s' for writing (%Rrc)"),
667 pszFilename, vrc);
668 }
669
670 /*
671 * Get the source file from the image maker.
672 */
673 HRESULT hrc;
674 RTVFSFILE hVfsSrcFile;
675 vrc = RTFsIsoMakerCreateVfsOutputFile(hIsoMaker, &hVfsSrcFile);
676 if (RT_SUCCESS(vrc))
677 {
678 RTVFSIOSTREAM hVfsSrcIso = RTVfsFileToIoStream(hVfsSrcFile);
679 RTVFSIOSTREAM hVfsDstIso = RTVfsFileToIoStream(hVfsDstFile);
680 if ( hVfsSrcIso != NIL_RTVFSIOSTREAM
681 && hVfsDstIso != NIL_RTVFSIOSTREAM)
682 {
683 vrc = RTVfsUtilPumpIoStreams(hVfsSrcIso, hVfsDstIso, 0 /*cbBufHint*/);
684 if (RT_SUCCESS(vrc))
685 hrc = S_OK;
686 else
687 hrc = mpParent->setErrorBoth(E_FAIL, vrc, tr("Error writing auxiliary ISO image '%s' (%Rrc)"),
688 pszFilename, vrc);
689 }
690 else
691 hrc = mpParent->setErrorBoth(E_FAIL, VERR_INTERNAL_ERROR_2,
692 tr("Internal Error: Failed to case VFS file to VFS I/O stream"));
693 RTVfsIoStrmRelease(hVfsSrcIso);
694 RTVfsIoStrmRelease(hVfsDstIso);
695 }
696 else
697 hrc = mpParent->setErrorBoth(E_FAIL, vrc, tr("RTFsIsoMakerCreateVfsOutputFile failed (%Rrc)"), vrc);
698 RTVfsFileRelease(hVfsSrcFile);
699 RTVfsFileRelease(hVfsDstFile);
700 if (FAILED(hrc))
701 RTFileDelete(pszFilename);
702 return hrc;
703}
704
705HRESULT UnattendedInstaller::addFilesToAuxVisoVectors(RTCList<RTCString> &rVecArgs, RTCList<RTCString> &rVecFiles,
706 RTVFS hVfsOrgIso, bool fOverwrite)
707{
708 RT_NOREF(hVfsOrgIso);
709
710 /*
711 * Save and add the scripts.
712 */
713 HRESULT hrc = addScriptToVisoVectors(&mMainScript, rVecArgs, rVecFiles, fOverwrite);
714 if (SUCCEEDED(hrc))
715 hrc = addScriptToVisoVectors(&mPostScript, rVecArgs, rVecFiles, fOverwrite);
716 if (SUCCEEDED(hrc))
717 {
718 try
719 {
720 /*
721 * If we've got a Guest Additions ISO, add its content to a /vboxadditions dir.
722 */
723 if (mpParent->i_getInstallGuestAdditions())
724 {
725 rVecArgs.append().append("--push-iso=").append(mpParent->i_getAdditionsIsoPath());
726 rVecArgs.append() = "/vboxadditions=/";
727 rVecArgs.append() = "--pop";
728 }
729
730 /*
731 * If we've got a Validation Kit ISO, add its content to a /vboxvalidationkit dir.
732 */
733 if (mpParent->i_getInstallTestExecService())
734 {
735 rVecArgs.append().append("--push-iso=").append(mpParent->i_getValidationKitIsoPath());
736 rVecArgs.append() = "/vboxvalidationkit=/";
737 rVecArgs.append() = "--pop";
738 }
739 }
740 catch (std::bad_alloc &)
741 {
742 hrc = E_OUTOFMEMORY;
743 }
744 }
745 return hrc;
746}
747
748HRESULT UnattendedInstaller::addScriptToVisoVectors(BaseTextScript *pEditor, RTCList<RTCString> &rVecArgs,
749 RTCList<RTCString> &rVecFiles, bool fOverwrite)
750{
751 /*
752 * Calc the aux script file name.
753 */
754 RTCString strScriptName;
755 try
756 {
757 strScriptName = mpParent->i_getAuxiliaryBasePath();
758 strScriptName.append(pEditor->getDefaultFilename());
759 }
760 catch (std::bad_alloc &)
761 {
762 return E_OUTOFMEMORY;
763 }
764
765 /*
766 * Save it.
767 */
768 HRESULT hrc = pEditor->save(strScriptName.c_str(), fOverwrite);
769 if (SUCCEEDED(hrc))
770 {
771 /*
772 * Add it to the vectors.
773 */
774 try
775 {
776 rVecArgs.append().append('/').append(pEditor->getDefaultFilename()).append('=').append(strScriptName);
777 rVecFiles.append(strScriptName);
778 }
779 catch (std::bad_alloc &)
780 {
781 RTFileDelete(strScriptName.c_str());
782 hrc = E_OUTOFMEMORY;
783 }
784 }
785 return hrc;
786}
787
788HRESULT UnattendedInstaller::finalizeAuxVisoFile(RTCList<RTCString> const &rVecArgs, const char *pszFilename, bool fOverwrite)
789{
790 /*
791 * Create a C-style argument vector and turn that into a command line string.
792 */
793 size_t const cArgs = rVecArgs.size();
794 const char **papszArgs = (const char **)RTMemTmpAlloc((cArgs + 1) * sizeof(const char *));
795 if (!papszArgs)
796 return E_OUTOFMEMORY;
797 for (size_t i = 0; i < cArgs; i++)
798 papszArgs[i] = rVecArgs[i].c_str();
799 papszArgs[cArgs] = NULL;
800
801 char *pszCmdLine;
802 int vrc = RTGetOptArgvToString(&pszCmdLine, papszArgs, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
803 RTMemTmpFree(papszArgs);
804 if (RT_FAILURE(vrc))
805 return mpParent->setErrorBoth(E_FAIL, vrc, tr("RTGetOptArgvToString failed (%Rrc)"), vrc);
806
807 /*
808 * Open the file.
809 */
810 HRESULT hrc;
811 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ;
812 if (fOverwrite)
813 fOpen |= RTFILE_O_CREATE_REPLACE;
814 else
815 fOpen |= RTFILE_O_CREATE;
816 RTFILE hFile;
817 vrc = RTFileOpen(&hFile, pszFilename, fOpen);
818 if (RT_SUCCESS(vrc))
819 {
820 vrc = RTFileWrite(hFile, pszCmdLine, strlen(pszCmdLine), NULL);
821 if (RT_SUCCESS(vrc))
822 vrc = RTFileClose(hFile);
823 else
824 RTFileClose(hFile);
825 if (RT_SUCCESS(vrc))
826 hrc = S_OK;
827 else
828 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Error writing '%s' (%Rrc)"), pszFilename, vrc);
829 }
830 else
831 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to create '%s' (%Rrc)"), pszFilename, vrc);
832
833 RTStrFree(pszCmdLine);
834 return hrc;
835}
836
837HRESULT UnattendedInstaller::loadAndParseFileFromIso(RTVFS hVfsOrgIso, const char *pszFilename, AbstractScript *pEditor)
838{
839 HRESULT hrc;
840 RTVFSFILE hVfsFile;
841 int vrc = RTVfsFileOpen(hVfsOrgIso, pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, &hVfsFile);
842 if (RT_SUCCESS(vrc))
843 {
844 hrc = pEditor->readFromHandle(hVfsFile, pszFilename);
845 RTVfsFileRelease(hVfsFile);
846 if (SUCCEEDED(hrc))
847 hrc = pEditor->parse();
848 }
849 else
850 hrc = mpParent->setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to open '%s' on the ISO '%s' (%Rrc)"),
851 pszFilename, mpParent->i_getIsoPath().c_str(), vrc);
852 return hrc;
853}
854
855
856
857//////////////////////////////////////////////////////////////////////////////////////////////////////
858/*
859*
860*
861* Implementation UnattendedLinuxInstaller functions
862*
863*/
864//////////////////////////////////////////////////////////////////////////////////////////////////////
865HRESULT UnattendedLinuxInstaller::editIsoLinuxCfg(GeneralTextScript *pEditor)
866{
867 try
868 {
869 /* Comment out 'display <filename>' directives that's used for displaying files at boot time. */
870 std::vector<size_t> vecLineNumbers = pEditor->findTemplate("display", RTCString::CaseInsensitive);
871 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
872 if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("display", RTCString::CaseInsensitive))
873 {
874 HRESULT hrc = pEditor->prependToLine(vecLineNumbers.at(i), "#");
875 if (FAILED(hrc))
876 return hrc;
877 }
878 }
879 catch (std::bad_alloc &)
880 {
881 return E_OUTOFMEMORY;
882 }
883 return editIsoLinuxCommon(pEditor);
884}
885
886HRESULT UnattendedLinuxInstaller::editIsoLinuxCommon(GeneralTextScript *pEditor)
887{
888 try
889 {
890 /* Set timeouts to 10 seconds. */
891 std::vector<size_t> vecLineNumbers = pEditor->findTemplate("timeout", RTCString::CaseInsensitive);
892 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
893 if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("timeout", RTCString::CaseInsensitive))
894 {
895 HRESULT hrc = pEditor->setContentOfLine(vecLineNumbers.at(i), "timeout 10");
896 if (FAILED(hrc))
897 return hrc;
898 }
899
900 /* Modify kernel parameters. */
901 vecLineNumbers = pEditor->findTemplate("append", RTCString::CaseInsensitive);
902 if (vecLineNumbers.size() > 0)
903 {
904 Utf8Str const &rStrAppend = mpParent->i_getExtraInstallKernelParameters().isNotEmpty()
905 ? mpParent->i_getExtraInstallKernelParameters()
906 : mStrDefaultExtraInstallKernelParameters;
907
908 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
909 if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("append", RTCString::CaseInsensitive))
910 {
911 Utf8Str strLine = pEditor->getContentOfLine(vecLineNumbers[i]);
912
913 /* Do removals. */
914 if (mArrStrRemoveInstallKernelParameters.size() > 0)
915 {
916 size_t offStart = strLine.find("append") + 5;
917 while (offStart < strLine.length() && !RT_C_IS_SPACE(strLine[offStart]))
918 offStart++;
919 while (offStart < strLine.length() && RT_C_IS_SPACE(strLine[offStart]))
920 offStart++;
921 if (offStart < strLine.length())
922 {
923 for (size_t iRemove = 0; iRemove < mArrStrRemoveInstallKernelParameters.size(); iRemove++)
924 {
925 RTCString const &rStrRemove = mArrStrRemoveInstallKernelParameters[iRemove];
926 for (size_t off = offStart; off < strLine.length(); )
927 {
928 Assert(!RT_C_IS_SPACE(strLine[off]));
929
930 /* Find the end of word. */
931 size_t offEnd = off + 1;
932 while (offEnd < strLine.length() && !RT_C_IS_SPACE(strLine[offEnd]))
933 offEnd++;
934
935 /* Check if it matches. */
936 if (RTStrSimplePatternNMatch(rStrRemove.c_str(), rStrRemove.length(),
937 strLine.c_str() + off, offEnd - off))
938 {
939 while (off > 0 && RT_C_IS_SPACE(strLine[off - 1]))
940 off--;
941 strLine.erase(off, offEnd - off);
942 }
943
944 /* Advance to the next word. */
945 off = offEnd;
946 while (off < strLine.length() && RT_C_IS_SPACE(strLine[off]))
947 off++;
948 }
949 }
950 }
951 }
952
953 /* Do the appending. */
954 if (rStrAppend.isNotEmpty())
955 {
956 if (!rStrAppend.startsWith(" ") && !strLine.endsWith(" "))
957 strLine.append(' ');
958 strLine.append(rStrAppend);
959 }
960
961 /* Update line. */
962 HRESULT hrc = pEditor->setContentOfLine(vecLineNumbers.at(i), strLine);
963 if (FAILED(hrc))
964 return hrc;
965 }
966 }
967 }
968 catch (std::bad_alloc &)
969 {
970 return E_OUTOFMEMORY;
971 }
972 return S_OK;
973}
974
975
976//////////////////////////////////////////////////////////////////////////////////////////////////////
977/*
978*
979*
980* Implementation UnattendedDebianInstaller functions
981*
982*/
983//////////////////////////////////////////////////////////////////////////////////////////////////////
984
985/**
986 * Helper for checking if a file exists.
987 * @todo promote to IPRT?
988 */
989static bool hlpVfsFileExists(RTVFS hVfs, const char *pszPath)
990{
991 RTFSOBJINFO ObjInfo;
992 int vrc = RTVfsQueryPathInfo(hVfs, pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
993 return RT_SUCCESS(vrc) && RTFS_IS_FILE(ObjInfo.Attr.fMode);
994}
995
996HRESULT UnattendedDebianInstaller::addFilesToAuxVisoVectors(RTCList<RTCString> &rVecArgs, RTCList<RTCString> &rVecFiles,
997 RTVFS hVfsOrgIso, bool fOverwrite)
998{
999 /*
1000 * Figure out the name of the menu config file that we have to edit.
1001 */
1002 bool fMenuConfigIsGrub = false;
1003 const char *pszMenuConfigFilename = "/isolinux/txt.cfg";
1004 if (!hlpVfsFileExists(hVfsOrgIso, pszMenuConfigFilename))
1005 {
1006 /* On Debian Live ISOs (at least from 9 to 11) the there is only menu.cfg. */
1007 if (hlpVfsFileExists(hVfsOrgIso, "/isolinux/menu.cfg"))
1008 pszMenuConfigFilename = "/isolinux/menu.cfg";
1009
1010 /* Ubuntus 21.10+ are UEFI only. No isolinux directory. We modify grub.cfg. */
1011 else if (hlpVfsFileExists(hVfsOrgIso, "/boot/grub/grub.cfg"))
1012 {
1013 pszMenuConfigFilename = "/boot/grub/grub.cfg";
1014 fMenuConfigIsGrub = true;
1015 }
1016 }
1017
1018 /* Check for existence of isolinux.cfg since UEFI-only ISOs do not have this file. */
1019 bool const fIsoLinuxCfgExists = hlpVfsFileExists(hVfsOrgIso, "isolinux/isolinux.cfg");
1020 Assert(!fIsoLinuxCfgExists || !fMenuConfigIsGrub); /** @todo r=bird: Perhaps prefix the hlpVfsFileExists call with 'fIsoLinuxCfgExists &&' above ? */
1021
1022 /*
1023 * VISO bits and filenames.
1024 */
1025 RTCString strIsoLinuxCfg;
1026 RTCString strTxtCfg;
1027 try
1028 {
1029 /* Remaster ISO. */
1030 rVecArgs.append() = "--no-file-mode";
1031 rVecArgs.append() = "--no-dir-mode";
1032
1033 rVecArgs.append() = "--import-iso";
1034 rVecArgs.append(mpParent->i_getIsoPath());
1035
1036 rVecArgs.append() = "--file-mode=0444";
1037 rVecArgs.append() = "--dir-mode=0555";
1038
1039 /* Replace the isolinux.cfg configuration file. */
1040 if (fIsoLinuxCfgExists)
1041 {
1042 /* First remove. */
1043 rVecArgs.append() = "isolinux/isolinux.cfg=:must-remove:";
1044 /* Then add the modified file. */
1045 strIsoLinuxCfg = mpParent->i_getAuxiliaryBasePath();
1046 strIsoLinuxCfg.append("isolinux-isolinux.cfg");
1047 rVecArgs.append().append("isolinux/isolinux.cfg=").append(strIsoLinuxCfg);
1048 }
1049
1050 /* Replace menu configuration file as well. */
1051 rVecArgs.append().assign(pszMenuConfigFilename).append("=:must-remove:");
1052 strTxtCfg = mpParent->i_getAuxiliaryBasePath();
1053 if (fMenuConfigIsGrub)
1054 strTxtCfg.append("grub.cfg");
1055 else
1056 strTxtCfg.append("isolinux-txt.cfg");
1057 rVecArgs.append().assign(pszMenuConfigFilename).append("=").append(strTxtCfg);
1058 }
1059 catch (std::bad_alloc &)
1060 {
1061 return E_OUTOFMEMORY;
1062 }
1063
1064 /*
1065 * Edit the isolinux.cfg file if it is there.
1066 */
1067 if (fIsoLinuxCfgExists)
1068 {
1069 GeneralTextScript Editor(mpParent);
1070 HRESULT hrc = loadAndParseFileFromIso(hVfsOrgIso, "/isolinux/isolinux.cfg", &Editor);
1071 if (SUCCEEDED(hrc))
1072 hrc = editIsoLinuxCfg(&Editor, RTPathFilename(pszMenuConfigFilename));
1073 if (SUCCEEDED(hrc))
1074 {
1075 hrc = Editor.save(strIsoLinuxCfg, fOverwrite);
1076 if (SUCCEEDED(hrc))
1077 {
1078 try
1079 {
1080 rVecFiles.append(strIsoLinuxCfg);
1081 }
1082 catch (std::bad_alloc &)
1083 {
1084 RTFileDelete(strIsoLinuxCfg.c_str());
1085 hrc = E_OUTOFMEMORY;
1086 }
1087 }
1088 }
1089 if (FAILED(hrc))
1090 return hrc;
1091 }
1092
1093 /*
1094 * Edit the menu config file.
1095 */
1096 {
1097 GeneralTextScript Editor(mpParent);
1098 HRESULT hrc = loadAndParseFileFromIso(hVfsOrgIso, pszMenuConfigFilename, &Editor);
1099 if (SUCCEEDED(hrc))
1100 {
1101 if (fMenuConfigIsGrub)
1102 hrc = editDebianGrubCfg(&Editor);
1103 else
1104 hrc = editDebianMenuCfg(&Editor);
1105 if (SUCCEEDED(hrc))
1106 {
1107 hrc = Editor.save(strTxtCfg, fOverwrite);
1108 if (SUCCEEDED(hrc))
1109 {
1110 try
1111 {
1112 rVecFiles.append(strTxtCfg);
1113 }
1114 catch (std::bad_alloc &)
1115 {
1116 RTFileDelete(strTxtCfg.c_str());
1117 hrc = E_OUTOFMEMORY;
1118 }
1119 }
1120 }
1121 }
1122 if (FAILED(hrc))
1123 return hrc;
1124 }
1125
1126 /*
1127 * Call parent to add the preseed file from mAlg.
1128 */
1129 return UnattendedLinuxInstaller::addFilesToAuxVisoVectors(rVecArgs, rVecFiles, hVfsOrgIso, fOverwrite);
1130}
1131
1132HRESULT UnattendedDebianInstaller::editIsoLinuxCfg(GeneralTextScript *pEditor, const char *pszMenuConfigFileName)
1133{
1134 try
1135 {
1136 /* Include menu config file. Since it can be txt.cfg, menu.cfg or something else we need to parametrize this. */
1137 if (pszMenuConfigFileName && pszMenuConfigFileName[0] != '\0')
1138 {
1139 std::vector<size_t> vecLineNumbers = pEditor->findTemplate("include", RTCString::CaseInsensitive);
1140 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
1141 {
1142 if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("include", RTCString::CaseInsensitive))
1143 {
1144 Utf8Str strIncludeLine("include ");
1145 strIncludeLine.append(pszMenuConfigFileName);
1146 HRESULT hrc = pEditor->setContentOfLine(vecLineNumbers.at(i), strIncludeLine);
1147 if (FAILED(hrc))
1148 return hrc;
1149 }
1150 }
1151 }
1152
1153 /* Comment out default directives since in Debian case default is handled in menu config file. */
1154 std::vector<size_t> vecLineNumbers = pEditor->findTemplate("default", RTCString::CaseInsensitive);
1155 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
1156 if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("default", RTCString::CaseInsensitive))
1157 {
1158 HRESULT hrc = pEditor->prependToLine(vecLineNumbers.at(i), "#");
1159 if (FAILED(hrc))
1160 return hrc;
1161 }
1162 }
1163 catch (std::bad_alloc &)
1164 {
1165 return E_OUTOFMEMORY;
1166 }
1167 return UnattendedLinuxInstaller::editIsoLinuxCfg(pEditor);
1168}
1169
1170HRESULT UnattendedDebianInstaller::editDebianMenuCfg(GeneralTextScript *pEditor)
1171{
1172 /*
1173 * Unlike Redhats, Debian variants define boot menu not in isolinux.cfg but some other
1174 * menu configuration files. They are mostly called txt.cfg and/or menu.cfg (and possibly some other names)
1175 * In this functions we attempt to set menu's default label (default menu item) to the one containing the word 'install'.
1176 */
1177 try
1178 {
1179 HRESULT hrc = S_OK;
1180 const char *pszNewLabel = "VBoxUnatendedInstall";
1181 std::vector<size_t> vecLineNumbers = pEditor->findTemplate("label", RTCString::CaseInsensitive);
1182 bool fLabelFound = false;
1183 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
1184 {
1185 RTCString const &rContent = pEditor->getContentOfLine(vecLineNumbers[i]);
1186 /* Skip this line if it does not start with the word 'label'. */
1187 if (!RTStrIStartsWith(rContent.c_str(), "label"))
1188 continue;
1189 /* Use the first menu item starting with word label and includes the word 'install'.*/
1190 if (RTStrIStr(rContent.c_str(), "install") != NULL)
1191 {
1192 /* Set the content of the line. It looks like multiple word labels (like label Debian Installer)
1193 * does not work very well in some cases. */
1194 Utf8Str strNewLabel("label ");
1195 strNewLabel.append(pszNewLabel);
1196 hrc = pEditor->setContentOfLine(vecLineNumbers[i], strNewLabel);
1197 if (SUCCEEDED(hrc))
1198 {
1199 fLabelFound = true;
1200 break;
1201 }
1202 }
1203 }
1204 if (!fLabelFound)
1205 hrc = E_FAIL;;
1206
1207 if (SUCCEEDED(hrc))
1208 {
1209 /* Modify the content of default lines so that they point to label we have chosen above. */
1210 Utf8Str strNewContent("default ");
1211 strNewContent.append(pszNewLabel);
1212
1213 std::vector<size_t> vecDefaultLineNumbers = pEditor->findTemplate("default", RTCString::CaseInsensitive);
1214 if (!vecDefaultLineNumbers.empty())
1215 {
1216 for (size_t j = 0; j < vecDefaultLineNumbers.size(); ++j)
1217 {
1218 hrc = pEditor->setContentOfLine(vecDefaultLineNumbers[j], strNewContent);
1219 if (FAILED(hrc))
1220 break;
1221 }
1222 }
1223 /* Add a defaul label line. */
1224 else
1225 hrc = pEditor->appendLine(strNewContent);
1226 }
1227 if (FAILED(hrc))
1228 return hrc;
1229 }
1230 catch (std::bad_alloc &)
1231 {
1232 return E_OUTOFMEMORY;
1233 }
1234 return UnattendedLinuxInstaller::editIsoLinuxCommon(pEditor);
1235}
1236
1237HRESULT UnattendedDebianInstaller::editDebianGrubCfg(GeneralTextScript *pEditor)
1238{
1239 /* Default menu entry of grub.cfg is set in /etc/deafult/grub file. */
1240 try
1241 {
1242 /* Set timeouts to 10 seconds. */
1243 std::vector<size_t> vecLineNumbers = pEditor->findTemplate("set timeout", RTCString::CaseInsensitive);
1244 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
1245 if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("set timeout", RTCString::CaseInsensitive))
1246 {
1247 HRESULT hrc = pEditor->setContentOfLine(vecLineNumbers.at(i), "set timeout=10");
1248 if (FAILED(hrc))
1249 return hrc;
1250 }
1251
1252 /* Modify kernel lines assuming that they starts with 'linux' keyword and 2nd word is the kernel command.*
1253 * we remove whatever comes after command and add our own command line options. */
1254 vecLineNumbers = pEditor->findTemplate("linux", RTCString::CaseInsensitive);
1255 if (vecLineNumbers.size() > 0)
1256 {
1257 Utf8Str const &rStrAppend = mpParent->i_getExtraInstallKernelParameters().isNotEmpty()
1258 ? mpParent->i_getExtraInstallKernelParameters()
1259 : mStrDefaultExtraInstallKernelParameters;
1260
1261 for (size_t i = 0; i < vecLineNumbers.size(); ++i)
1262 {
1263 HRESULT hrc = S_OK;
1264 if (pEditor->getContentOfLine(vecLineNumbers[i]).startsWithWord("linux", RTCString::CaseInsensitive))
1265 {
1266 Utf8Str strLine = pEditor->getContentOfLine(vecLineNumbers[i]);
1267 size_t cbPos = strLine.find("linux") + strlen("linux");
1268 bool fSecondWord = false;
1269 /* Find the end of 2nd word assuming that it is kernel command. */
1270 while (cbPos < strLine.length())
1271 {
1272 if (!fSecondWord)
1273 {
1274 if (strLine[cbPos] != '\t' && strLine[cbPos] != ' ')
1275 fSecondWord = true;
1276 }
1277 else
1278 {
1279 if (strLine[cbPos] == '\t' || strLine[cbPos] == ' ')
1280 break;
1281 }
1282 ++cbPos;
1283 }
1284 if (!fSecondWord)
1285 hrc = E_FAIL;
1286
1287 if (SUCCEEDED(hrc))
1288 {
1289 strLine.erase(cbPos, strLine.length() - cbPos);
1290
1291 /* Do the appending. */
1292 if (rStrAppend.isNotEmpty())
1293 {
1294 if (!rStrAppend.startsWith(" ") && !strLine.endsWith(" "))
1295 strLine.append(' ');
1296 strLine.append(rStrAppend);
1297 }
1298
1299 /* Update line. */
1300 hrc = pEditor->setContentOfLine(vecLineNumbers.at(i), strLine);
1301 }
1302 if (FAILED(hrc))
1303 return hrc;
1304 }
1305 }
1306 }
1307 }
1308 catch (std::bad_alloc &)
1309 {
1310 return E_OUTOFMEMORY;
1311 }
1312 return S_OK;
1313}
1314
1315//////////////////////////////////////////////////////////////////////////////////////////////////////
1316/*
1317*
1318*
1319* Implementation UnattendedRhel6Installer functions
1320*
1321*/
1322//////////////////////////////////////////////////////////////////////////////////////////////////////
1323HRESULT UnattendedRhel6Installer::addFilesToAuxVisoVectors(RTCList<RTCString> &rVecArgs, RTCList<RTCString> &rVecFiles,
1324 RTVFS hVfsOrgIso, bool fOverwrite)
1325{
1326 Utf8Str strIsoLinuxCfg;
1327 try
1328 {
1329#if 1
1330 /* Remaster ISO. */
1331 rVecArgs.append() = "--no-file-mode";
1332 rVecArgs.append() = "--no-dir-mode";
1333
1334 rVecArgs.append() = "--import-iso";
1335 rVecArgs.append(mpParent->i_getIsoPath());
1336
1337 rVecArgs.append() = "--file-mode=0444";
1338 rVecArgs.append() = "--dir-mode=0555";
1339
1340 /* We replace isolinux.cfg with our edited version (see further down). */
1341 rVecArgs.append() = "isolinux/isolinux.cfg=:must-remove:";
1342 strIsoLinuxCfg = mpParent->i_getAuxiliaryBasePath();
1343 strIsoLinuxCfg.append("isolinux-isolinux.cfg");
1344 rVecArgs.append().append("isolinux/isolinux.cfg=").append(strIsoLinuxCfg);
1345
1346#else
1347 /** @todo Maybe we should just remaster the ISO for redhat derivatives too?
1348 * One less CDROM to mount. */
1349 /* Name the ISO. */
1350 rVecArgs.append() = "--volume-id=VBox Unattended Boot";
1351
1352 /* Copy the isolinux directory from the original install ISO. */
1353 rVecArgs.append().append("--push-iso=").append(mpParent->i_getIsoPath());
1354 rVecArgs.append() = "/isolinux=/isolinux";
1355 rVecArgs.append() = "--pop";
1356
1357 /* We replace isolinux.cfg with our edited version (see further down). */
1358 rVecArgs.append() = "/isolinux/isolinux.cfg=:must-remove:";
1359
1360 strIsoLinuxCfg = mpParent->i_getAuxiliaryBasePath();
1361 strIsoLinuxCfg.append("isolinux-isolinux.cfg");
1362 rVecArgs.append().append("/isolinux/isolinux.cfg=").append(strIsoLinuxCfg);
1363
1364 /* Configure booting /isolinux/isolinux.bin. */
1365 rVecArgs.append() = "--eltorito-boot";
1366 rVecArgs.append() = "/isolinux/isolinux.bin";
1367 rVecArgs.append() = "--no-emulation-boot";
1368 rVecArgs.append() = "--boot-info-table";
1369 rVecArgs.append() = "--boot-load-seg=0x07c0";
1370 rVecArgs.append() = "--boot-load-size=4";
1371
1372 /* Make the boot catalog visible in the file system. */
1373 rVecArgs.append() = "--boot-catalog=/isolinux/vboxboot.cat";
1374#endif
1375 }
1376 catch (std::bad_alloc &)
1377 {
1378 return E_OUTOFMEMORY;
1379 }
1380
1381 /*
1382 * Edit isolinux.cfg and save it.
1383 */
1384 {
1385 GeneralTextScript Editor(mpParent);
1386 HRESULT hrc = loadAndParseFileFromIso(hVfsOrgIso, "/isolinux/isolinux.cfg", &Editor);
1387 if (SUCCEEDED(hrc))
1388 hrc = editIsoLinuxCfg(&Editor);
1389 if (SUCCEEDED(hrc))
1390 {
1391 hrc = Editor.save(strIsoLinuxCfg, fOverwrite);
1392 if (SUCCEEDED(hrc))
1393 {
1394 try
1395 {
1396 rVecFiles.append(strIsoLinuxCfg);
1397 }
1398 catch (std::bad_alloc &)
1399 {
1400 RTFileDelete(strIsoLinuxCfg.c_str());
1401 hrc = E_OUTOFMEMORY;
1402 }
1403 }
1404 }
1405 if (FAILED(hrc))
1406 return hrc;
1407 }
1408
1409 /*
1410 * Call parent to add the ks.cfg file from mAlg.
1411 */
1412 return UnattendedLinuxInstaller::addFilesToAuxVisoVectors(rVecArgs, rVecFiles, hVfsOrgIso, fOverwrite);
1413}
1414
1415
1416//////////////////////////////////////////////////////////////////////////////////////////////////////
1417/*
1418*
1419*
1420* Implementation UnattendedSuseInstaller functions
1421*
1422*/
1423//////////////////////////////////////////////////////////////////////////////////////////////////////
1424#if 0 /* doesn't work, so convert later */
1425/*
1426 *
1427 * UnattendedSuseInstaller protected methods
1428 *
1429*/
1430HRESULT UnattendedSuseInstaller::setUserData()
1431{
1432 HRESULT rc = S_OK;
1433 //here base class function must be called first
1434 //because user home directory is set after user name
1435 rc = UnattendedInstaller::setUserData();
1436
1437 rc = mAlg->setField(USERHOMEDIR_ID, "");
1438 if (FAILED(rc))
1439 return rc;
1440
1441 return rc;
1442}
1443
1444/*
1445 *
1446 * UnattendedSuseInstaller private methods
1447 *
1448*/
1449
1450HRESULT UnattendedSuseInstaller::iv_initialPhase()
1451{
1452 Assert(isAuxiliaryIsoNeeded());
1453 if (mParent->i_isGuestOs64Bit())
1454 mFilesAndDirsToExtractFromIso.append("boot/x86_64/loader/ ");
1455 else
1456 mFilesAndDirsToExtractFromIso.append("boot/i386/loader/ ");
1457 return extractOriginalIso(mFilesAndDirsToExtractFromIso);
1458}
1459
1460
1461HRESULT UnattendedSuseInstaller::setupScriptOnAuxiliaryCD(const Utf8Str &path)
1462{
1463 HRESULT rc = S_OK;
1464
1465 GeneralTextScript isoSuseCfgScript(mpParent);
1466 rc = isoSuseCfgScript.read(path);
1467 rc = isoSuseCfgScript.parse();
1468 //fix linux core bootable parameters: add path to the preseed script
1469
1470 std::vector<size_t> listOfLines = isoSuseCfgScript.findTemplate("append");
1471 for(unsigned int i=0; i<listOfLines.size(); ++i)
1472 {
1473 isoSuseCfgScript.appendToLine(listOfLines.at(i),
1474 " auto=true priority=critical autoyast=default instmode=cd quiet splash noprompt noshell --");
1475 }
1476
1477 //find all lines with "label" inside
1478 listOfLines = isoSuseCfgScript.findTemplate("label");
1479 for(unsigned int i=0; i<listOfLines.size(); ++i)
1480 {
1481 Utf8Str content = isoSuseCfgScript.getContentOfLine(listOfLines.at(i));
1482
1483 //suppose general string looks like "label linux", two words separated by " ".
1484 RTCList<RTCString> partsOfcontent = content.split(" ");
1485
1486 if (partsOfcontent.at(1).contains("linux"))
1487 {
1488 std::vector<size_t> listOfDefault = isoSuseCfgScript.findTemplate("default");
1489 //handle the lines more intelligently
1490 for(unsigned int j=0; j<listOfDefault.size(); ++j)
1491 {
1492 Utf8Str newContent("default ");
1493 newContent.append(partsOfcontent.at(1));
1494 isoSuseCfgScript.setContentOfLine(listOfDefault.at(j), newContent);
1495 }
1496 }
1497 }
1498
1499 rc = isoSuseCfgScript.save(path, true);
1500
1501 LogRelFunc(("UnattendedSuseInstaller::setupScriptsOnAuxiliaryCD(): The file %s has been changed\n", path.c_str()));
1502
1503 return rc;
1504}
1505#endif
1506
1507
1508//////////////////////////////////////////////////////////////////////////////////////////////////////
1509/*
1510*
1511*
1512* Implementation UnattendedFreeBsdInstaller functions
1513*
1514*/
1515//////////////////////////////////////////////////////////////////////////////////////////////////////
1516HRESULT UnattendedFreeBsdInstaller::addFilesToAuxVisoVectors(RTCList<RTCString> &rVecArgs, RTCList<RTCString> &rVecFiles,
1517 RTVFS hVfsOrgIso, bool fOverwrite)
1518{
1519 try
1520 {
1521 RTCString strScriptName;
1522 strScriptName = mpParent->i_getAuxiliaryBasePath();
1523 strScriptName.append(mMainScript.getDefaultFilename());
1524
1525 /* Need to retain the original file permissions for executables. */
1526 rVecArgs.append() = "--no-file-mode";
1527 rVecArgs.append() = "--no-dir-mode";
1528
1529 rVecArgs.append() = "--import-iso";
1530 rVecArgs.append(mpParent->i_getIsoPath());
1531
1532 rVecArgs.append() = "--file-mode=0444";
1533 rVecArgs.append() = "--dir-mode=0555";
1534
1535 /* Remaster ISO, the installer config has to go into /etc. */
1536 rVecArgs.append().append("/etc/installerconfig=").append(strScriptName);
1537 }
1538 catch (std::bad_alloc &)
1539 {
1540 return E_OUTOFMEMORY;
1541 }
1542
1543 /*
1544 * Call parent to add the remaining files
1545 */
1546 return UnattendedInstaller::addFilesToAuxVisoVectors(rVecArgs, rVecFiles, hVfsOrgIso, fOverwrite);
1547}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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