VirtualBox

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

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

scm copyright and license note update

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