VirtualBox

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

Last change on this file since 97698 was 96706, checked in by vboxsync, 2 years ago

Unattended: bugref:9781. Some modifications on OS detection and menu config file editing to enable unattended install for Linux Mint.

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