VirtualBox

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

Last change on this file since 101633 was 101633, checked in by vboxsync, 12 months ago

Main/Unattended: Support debian arm64 (tested with Debian 11 netinst) images, bugref:10516

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