VirtualBox

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

Last change on this file since 101351 was 101351, checked in by vboxsync, 13 months ago

Unattended: bugref:10530. Adapting kickstart file and kernel command line arguments to the new syntax introduced with OL9.

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