VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/UnattendedScript.cpp@ 68202

Last change on this file since 68202 was 68164, checked in by vboxsync, 8 years ago

Unattended: Changed boot order to work around DVD eject issues with fedora 26, HD is now first DVD/flpppy next (assuming HD isn't bootable till after first installation reboot). Expose IMachine::RTCUseUTC setting to the script templates. Post install fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.0 KB
Line 
1/* $Id: UnattendedScript.cpp 68164 2017-07-28 20:59:17Z vboxsync $ */
2/** @file
3 * Implementeation of algorithms which read/parse/save scripts for unattended installation.
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*********************************************************************************************************************************
19* Header Files *
20*********************************************************************************************************************************/
21#define LOG_GROUP LOG_GROUP_MAIN_UNATTENDED
22#include "LoggingNew.h"
23#include "VirtualBoxBase.h"
24#include "AutoCaller.h"
25#include <VBox/com/ErrorInfo.h>
26
27#include "MachineImpl.h"
28#include "UnattendedScript.h"
29#include "UnattendedImpl.h"
30
31#include <VBox/err.h>
32
33#include <iprt/ctype.h>
34#include <iprt/file.h>
35#include <iprt/getopt.h>
36#include <iprt/path.h>
37
38using namespace std;
39
40
41//////////////////////////////////////////////////////////////////////////////////////////////////////
42/*
43*
44*
45* Implementation BaseTextScript functions
46*
47*/
48//////////////////////////////////////////////////////////////////////////////////////////////////////
49HRESULT BaseTextScript::read(const Utf8Str &rStrFilename)
50{
51 /*
52 * Open the file for reading and figure it's size. Capping the size
53 * at 16MB so we don't exaust the heap on bad input.
54 */
55 HRESULT hrc;
56 RTVFSFILE hVfsFile;
57 int vrc = RTVfsFileOpenNormal(rStrFilename.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, &hVfsFile);
58 if (RT_SUCCESS(vrc))
59 {
60 hrc = readFromHandle(hVfsFile, rStrFilename.c_str());
61 RTVfsFileRelease(hVfsFile);
62 }
63 else
64 hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("Failed to open '%s' (%Rrc)"), rStrFilename.c_str(), vrc);
65 return hrc;
66}
67
68HRESULT BaseTextScript::readFromHandle(RTVFSFILE hVfsFile, const char *pszFilename)
69{
70 /*
71 * Open the file for reading and figure it's size. Capping the size
72 * at 16MB so we don't exaust the heap on bad input.
73 */
74 HRESULT hrc;
75 uint64_t cbFile = 0;
76 int vrc = RTVfsFileGetSize(hVfsFile, &cbFile);
77 if ( RT_SUCCESS(vrc)
78 && cbFile < _16M)
79 {
80 /*
81 * Exploint the jolt() feature of RTCString and read the content directly into
82 * its storage buffer.
83 */
84 vrc = mStrScriptFullContent.reserveNoThrow((size_t)cbFile + 1);
85 if (RT_SUCCESS(vrc))
86 {
87 char *pszDst = mStrScriptFullContent.mutableRaw();
88 vrc = RTVfsFileReadAt(hVfsFile, 0 /*off*/, pszDst, (size_t)cbFile, NULL);
89 pszDst[(size_t)cbFile] = '\0';
90 if (RT_SUCCESS(vrc))
91 {
92 /*
93 * We must validate the encoding or we'll be subject to potential security trouble.
94 * If this turns out to be problematic, we will need to implement codeset
95 * conversion coping mechanisms.
96 */
97 vrc = RTStrValidateEncodingEx(pszDst, (size_t)cbFile + 1,
98 RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED | RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
99 if (RT_SUCCESS(vrc))
100 {
101 mStrScriptFullContent.jolt();
102 return S_OK;
103 }
104
105 hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("'%s' isn't valid UTF-8: %Rrc"), pszFilename, vrc);
106 }
107 else
108 hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("Error reading '%s': %Rrc"), pszFilename, vrc);
109 mStrScriptFullContent.setNull();
110 }
111 else
112 hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("Failed to allocate memory (%'RU64 bytes) for '%s'"),
113 cbFile, pszFilename);
114 }
115 else if (RT_SUCCESS(vrc))
116 hrc = mpSetError->setErrorVrc(VERR_FILE_TOO_BIG,
117 mpSetError->tr("'%s' is too big (max 16MB): %'RU64"), pszFilename, cbFile);
118 else
119 hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("RTVfsFileGetSize failed (%Rrc)"), vrc);
120 return hrc;
121}
122
123HRESULT BaseTextScript::save(const Utf8Str &rStrFilename, bool fOverwrite)
124{
125 /*
126 * We may have to append the default filename to the
127 */
128 const char *pszFilename = rStrFilename.c_str();
129 Utf8Str strWithDefaultFilename;
130 if ( getDefaultFilename() != NULL
131 && *getDefaultFilename() != '\0'
132 && RTDirExists(rStrFilename.c_str()) )
133 {
134 try
135 {
136 strWithDefaultFilename = rStrFilename;
137 strWithDefaultFilename.append(RTPATH_SLASH);
138 strWithDefaultFilename.append(getDefaultFilename());
139 }
140 catch (std::bad_alloc)
141 {
142 return E_OUTOFMEMORY;
143 }
144 pszFilename = strWithDefaultFilename.c_str();
145 }
146
147 /*
148 * Save the filename for later use.
149 */
150 try
151 {
152 mStrSavedPath = pszFilename;
153 }
154 catch (std::bad_alloc)
155 {
156 return E_OUTOFMEMORY;
157 }
158
159 /*
160 * Use the saveToString method to produce the content.
161 */
162 Utf8Str strDst;
163 HRESULT hrc = saveToString(strDst);
164 if (SUCCEEDED(hrc))
165 {
166 /*
167 * Write the content.
168 */
169 RTFILE hFile;
170 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_ALL;
171 if (fOverwrite)
172 fOpen |= RTFILE_O_CREATE_REPLACE;
173 else
174 fOpen |= RTFILE_O_CREATE;
175 int vrc = RTFileOpen(&hFile, pszFilename, fOpen);
176 if (RT_SUCCESS(vrc))
177 {
178 vrc = RTFileWrite(hFile, strDst.c_str(), strDst.length(), NULL);
179 if (RT_SUCCESS(vrc))
180 {
181 vrc = RTFileClose(hFile);
182 if (RT_SUCCESS(vrc))
183 {
184 LogRelFlow(("GeneralTextScript::save(): saved %zu bytes to '%s'\n", strDst.length(), pszFilename));
185 return S_OK;
186 }
187 }
188 RTFileClose(hFile);
189 RTFileDelete(pszFilename);
190 hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("Error writing to '%s' (%Rrc)"), pszFilename, vrc);
191 }
192 else
193 hrc = mpSetError->setErrorVrc(vrc, mpSetError->tr("Error creating/replacing '%s' (%Rrc)"), pszFilename, vrc);
194 }
195 return hrc;
196}
197
198//////////////////////////////////////////////////////////////////////////////////////////////////////
199/*
200*
201*
202* Implementation UnattendedScriptTemplate methods
203*
204*/
205//////////////////////////////////////////////////////////////////////////////////////////////////////
206
207UnattendedScriptTemplate::UnattendedScriptTemplate(Unattended *pUnattended, const char *pszDefaultTemplateFilename,
208 const char *pszDefaultFilename)
209 : BaseTextScript(pUnattended, pszDefaultTemplateFilename, pszDefaultFilename), mpUnattended(pUnattended)
210{
211}
212
213
214HRESULT UnattendedScriptTemplate::saveToString(Utf8Str &rStrDst)
215{
216 static const char s_szPrefix[] = "@@VBOX_";
217 static const char s_szPrefixInsert[] = "@@VBOX_INSERT_";
218 static const char s_szPrefixCond[] = "@@VBOX_COND_";
219 static const char s_szPrefixCondEnd[] = "@@VBOX_COND_END@@";
220
221 struct
222 {
223 bool fSavedOutputting;
224 } aConds[8];
225 unsigned cConds = 0;
226 bool fOutputting = true;
227 HRESULT hrc = E_FAIL;
228 size_t offTemplate = 0;
229 size_t cchTemplate = mStrScriptFullContent.length();
230 rStrDst.setNull();
231 for (;;)
232 {
233 /*
234 * Find the next placeholder and add any text before it to the output.
235 */
236 size_t offPlaceholder = mStrScriptFullContent.find(s_szPrefix, offTemplate);
237 size_t cchToCopy = offPlaceholder != RTCString::npos ? offPlaceholder - offTemplate : cchTemplate - offTemplate;
238 if (cchToCopy > 0)
239 {
240 if (fOutputting)
241 {
242 try
243 {
244 rStrDst.append(mStrScriptFullContent, offTemplate, cchToCopy);
245 }
246 catch (std::bad_alloc)
247 {
248 hrc = E_OUTOFMEMORY;
249 break;
250 }
251 }
252 offTemplate += cchToCopy;
253 }
254
255 /*
256 * Process placeholder.
257 */
258 if (offPlaceholder != RTCString::npos)
259 {
260 /*
261 * First we must find the end of the placeholder string.
262 */
263 const char *pszPlaceholder = mStrScriptFullContent.c_str() + offPlaceholder;
264 size_t cchPlaceholder = sizeof(s_szPrefix) - 1;
265 char ch;
266 while ( offPlaceholder + cchPlaceholder < cchTemplate
267 && (ch = pszPlaceholder[cchPlaceholder]) != '\0'
268 && ( ch == '_'
269 || RT_C_IS_UPPER(ch)
270 || RT_C_IS_DIGIT(ch)) )
271 cchPlaceholder++;
272
273 if ( offPlaceholder + cchPlaceholder < cchTemplate
274 && pszPlaceholder[cchPlaceholder] == '@')
275 {
276 cchPlaceholder++;
277 if ( offPlaceholder + cchPlaceholder < cchTemplate
278 && pszPlaceholder[cchPlaceholder] == '@')
279 cchPlaceholder++;
280 }
281
282 if ( pszPlaceholder[cchPlaceholder - 1] != '@'
283 || pszPlaceholder[cchPlaceholder - 2] != '@'
284 || ( strncmp(pszPlaceholder, s_szPrefixInsert, sizeof(s_szPrefixInsert) - 1) != 0
285 && strncmp(pszPlaceholder, s_szPrefixCond, sizeof(s_szPrefixCond) - 1) != 0) )
286 {
287 hrc = mpSetError->setError(E_FAIL, mpSetError->tr("Malformed template placeholder '%.*s'"),
288 cchPlaceholder, pszPlaceholder);
289 break;
290 }
291
292 offTemplate += cchPlaceholder;
293
294 /*
295 * @@VBOX_INSERT_XXX@@:
296 */
297 if (strncmp(pszPlaceholder, s_szPrefixInsert, sizeof(s_szPrefixInsert) - 1) == 0)
298 {
299 /*
300 * Get the placeholder value and add it to the output.
301 */
302 RTCString strValue;
303 hrc = getReplacement(pszPlaceholder, cchPlaceholder, fOutputting, strValue);
304 if (SUCCEEDED(hrc))
305 {
306 if (fOutputting)
307 {
308 try
309 {
310 rStrDst.append(strValue);
311 }
312 catch (std::bad_alloc)
313 {
314 hrc = E_OUTOFMEMORY;
315 break;
316 }
317 }
318 }
319 else
320 break;
321 }
322 /*
323 * @@VBOX_COND_END@@: Pop one item of the conditional stack.
324 */
325 else if ( cchPlaceholder == sizeof(s_szPrefixCondEnd) - 1U
326 && strncmp(pszPlaceholder, s_szPrefixCondEnd, sizeof(s_szPrefixCondEnd) - 1U) == 0)
327 {
328 if (cConds > 0)
329 {
330 cConds--;
331 fOutputting = aConds[cConds].fSavedOutputting;
332 }
333 else
334 {
335 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
336 mpSetError->tr("%s without @@VBOX_COND_XXX@@ at offset %zu (%#zx)"),
337 s_szPrefixCondEnd, offPlaceholder, offPlaceholder);
338 break;
339 }
340 }
341 /*
342 * @@VBOX_COND_XXX@@: Push the previous outputting state and combine it with the
343 * one from the condition.
344 */
345 else
346 {
347 Assert(strncmp(pszPlaceholder, s_szPrefixCond, sizeof(s_szPrefixCond) - 1) == 0);
348 if (cConds + 1 < RT_ELEMENTS(aConds))
349 {
350 aConds[cConds].fSavedOutputting = fOutputting;
351 bool fNewOutputting = fOutputting;
352 hrc = getConditional(pszPlaceholder, cchPlaceholder, &fNewOutputting);
353 if (SUCCEEDED(hrc))
354 fOutputting = fOutputting && fNewOutputting;
355 else
356 break;
357 cConds++;
358 }
359 else
360 {
361 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
362 mpSetError->tr("Too deep conditional nesting at offset %zu (%#zx)"),
363 offPlaceholder, offPlaceholder);
364 break;
365 }
366 }
367 }
368
369 /*
370 * Done?
371 */
372 if (offTemplate >= cchTemplate)
373 {
374 if (cConds == 0)
375 return S_OK;
376 if (cConds == 1)
377 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, mpSetError->tr("Missing @@VBOX_COND_END@@"));
378 else
379 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, mpSetError->tr("Missing %u @@VBOX_COND_END@@"), cConds);
380 break;
381 }
382 }
383
384 /* failed */
385 rStrDst.setNull();
386 return hrc;
387}
388
389HRESULT UnattendedScriptTemplate::getReplacement(const char *pachPlaceholder, size_t cchPlaceholder,
390 bool fOutputting, RTCString &rValue)
391{
392 /*
393 * Check for an escaping suffix. Drop the '@@'.
394 */
395 size_t const cchFullPlaceholder = cchPlaceholder;
396 enum
397 {
398 kValueEscaping_None,
399 kValueEscaping_Bourne,
400 kValueEscaping_XML_Element,
401 kValueEscaping_XML_Attribute_Double_Quotes
402 } enmEscaping;
403
404#define PLACEHOLDER_ENDS_WITH(a_szSuffix) \
405 ( cchPlaceholder > sizeof(a_szSuffix) - 1U \
406 && memcmp(&pachPlaceholder[cchPlaceholder - sizeof(a_szSuffix) + 1U], a_szSuffix, sizeof(a_szSuffix) - 1U) == 0)
407 if (PLACEHOLDER_ENDS_WITH("_SH@@"))
408 {
409 cchPlaceholder -= 3 + 2;
410 enmEscaping = kValueEscaping_Bourne;
411 }
412 else if (PLACEHOLDER_ENDS_WITH("_ELEMENT@@"))
413 {
414 cchPlaceholder -= 8 + 2;
415 enmEscaping = kValueEscaping_XML_Element;
416 }
417 else if (PLACEHOLDER_ENDS_WITH("_ATTRIB_DQ@@"))
418 {
419 cchPlaceholder -= 10 + 2;
420 enmEscaping = kValueEscaping_XML_Attribute_Double_Quotes;
421 }
422 else
423 {
424 Assert(PLACEHOLDER_ENDS_WITH("@@"));
425 cchPlaceholder -= 2;
426 enmEscaping = kValueEscaping_None;
427 }
428
429 /*
430 * Resolve and escape the value.
431 */
432 HRESULT hrc;
433 try
434 {
435 switch (enmEscaping)
436 {
437 case kValueEscaping_None:
438 hrc = getUnescapedReplacement(pachPlaceholder, cchPlaceholder, cchFullPlaceholder, fOutputting, rValue);
439 if (SUCCEEDED(hrc))
440 return hrc;
441 break;
442
443 case kValueEscaping_Bourne:
444 case kValueEscaping_XML_Element:
445 case kValueEscaping_XML_Attribute_Double_Quotes:
446 {
447 RTCString strUnescaped;
448 hrc = getUnescapedReplacement(pachPlaceholder, cchPlaceholder, cchFullPlaceholder, fOutputting, strUnescaped);
449 if (SUCCEEDED(hrc))
450 {
451 switch (enmEscaping)
452 {
453 case kValueEscaping_Bourne:
454 {
455 const char * const papszArgs[2] = { strUnescaped.c_str(), NULL };
456 char *pszEscaped = NULL;
457 int vrc = RTGetOptArgvToString(&pszEscaped, papszArgs, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
458 if (RT_SUCCESS(vrc))
459 {
460 try
461 {
462 rValue = pszEscaped;
463 RTStrFree(pszEscaped);
464 return S_OK;
465 }
466 catch (std::bad_alloc)
467 {
468 hrc = E_OUTOFMEMORY;
469 }
470 RTStrFree(pszEscaped);
471 }
472 break;
473 }
474
475 case kValueEscaping_XML_Element:
476 rValue.printf("%RMes", strUnescaped.c_str());
477 return S_OK;
478
479 case kValueEscaping_XML_Attribute_Double_Quotes:
480 {
481 RTCString strTmp;
482 strTmp.printf("%RMas", strUnescaped.c_str());
483 rValue = RTCString(strTmp, 1, strTmp.length() - 2);
484 return S_OK;
485 }
486
487 default:
488 hrc = E_FAIL;
489 break;
490 }
491 }
492 break;
493 }
494
495 default:
496 AssertFailedStmt(hrc = E_FAIL);
497 break;
498 }
499 }
500 catch (std::bad_alloc)
501 {
502 hrc = E_OUTOFMEMORY;
503 }
504 rValue.setNull();
505 return hrc;
506}
507
508HRESULT UnattendedScriptTemplate::getUnescapedReplacement(const char *pachPlaceholder, size_t cchPlaceholder,
509 size_t cchFullPlaceholder, bool fOutputting, RTCString &rValue)
510{
511 RT_NOREF(fOutputting);
512#define IS_PLACEHOLDER_MATCH(a_szMatch) \
513 ( cchPlaceholder == sizeof("@@VBOX_INSERT_" a_szMatch) - 1U \
514 && memcmp(pachPlaceholder, "@@VBOX_INSERT_" a_szMatch, sizeof("@@VBOX_INSERT_" a_szMatch) - 1U) == 0)
515
516 if (IS_PLACEHOLDER_MATCH("USER_LOGIN"))
517 rValue = mpUnattended->i_getUser();
518 else if (IS_PLACEHOLDER_MATCH("USER_PASSWORD"))
519 rValue = mpUnattended->i_getPassword();
520 else if (IS_PLACEHOLDER_MATCH("ROOT_PASSWORD"))
521 rValue = mpUnattended->i_getPassword();
522 else if (IS_PLACEHOLDER_MATCH("USER_FULL_NAME"))
523 rValue = mpUnattended->i_getFullUserName();
524 else if (IS_PLACEHOLDER_MATCH("PRODUCT_KEY"))
525 rValue = mpUnattended->i_getProductKey();
526 else if (IS_PLACEHOLDER_MATCH("POST_INSTALL_COMMAND"))
527 rValue = mpUnattended->i_getPostInstallCommand();
528 else if (IS_PLACEHOLDER_MATCH("IMAGE_INDEX"))
529 rValue.printf("%u", mpUnattended->i_getImageIndex());
530 else if (IS_PLACEHOLDER_MATCH("OS_ARCH"))
531 rValue = mpUnattended->i_isGuestOs64Bit() ? "amd64" : "x86";
532 else if (IS_PLACEHOLDER_MATCH("OS_ARCH2"))
533 rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "x86";
534 else if (IS_PLACEHOLDER_MATCH("OS_ARCH3"))
535 rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i386";
536 else if (IS_PLACEHOLDER_MATCH("OS_ARCH4"))
537 rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i486";
538 else if (IS_PLACEHOLDER_MATCH("OS_ARCH6"))
539 rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i686";
540 else if (IS_PLACEHOLDER_MATCH("TIME_ZONE_UX"))
541 rValue = mpUnattended->i_getTimeZoneInfo()
542 ? mpUnattended->i_getTimeZoneInfo()->pszUnixName : mpUnattended->i_getTimeZone();
543 else if (IS_PLACEHOLDER_MATCH("TIME_ZONE_WIN_NAME"))
544 {
545 PCRTTIMEZONEINFO pInfo = mpUnattended->i_getTimeZoneInfo();
546 if (pInfo)
547 rValue = pInfo->pszWindowsName ? pInfo->pszWindowsName : "GMT";
548 else
549 rValue = mpUnattended->i_getTimeZone();
550 }
551 else if (IS_PLACEHOLDER_MATCH("TIME_ZONE_WIN_INDEX"))
552 {
553 PCRTTIMEZONEINFO pInfo = mpUnattended->i_getTimeZoneInfo();
554 if (pInfo)
555 rValue.printf("%u", pInfo->idxWindows ? pInfo->idxWindows : 85 /*GMT*/);
556 else
557 rValue = mpUnattended->i_getTimeZone();
558 }
559 else if (IS_PLACEHOLDER_MATCH("LOCALE"))
560 rValue = mpUnattended->i_getLocale();
561 else if (IS_PLACEHOLDER_MATCH("COUNTRY"))
562 rValue = mpUnattended->i_getCountry();
563 else if (IS_PLACEHOLDER_MATCH("HOSTNAME_FQDN"))
564 rValue = mpUnattended->i_getHostname();
565 else if (IS_PLACEHOLDER_MATCH("HOSTNAME_WITHOUT_DOMAIN"))
566 rValue.assign(mpUnattended->i_getHostname(), 0, mpUnattended->i_getHostname().find("."));
567 else if (IS_PLACEHOLDER_MATCH("HOSTNAME_DOMAIN"))
568 rValue.assign(mpUnattended->i_getHostname(), mpUnattended->i_getHostname().find(".") + 1);
569 else
570 {
571 rValue.setNull();
572 return mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, mpSetError->tr("Unknown template placeholder '%.*s'"),
573 cchFullPlaceholder, pachPlaceholder);
574 }
575 return S_OK;
576#undef IS_PLACEHOLDER_MATCH
577}
578
579HRESULT UnattendedScriptTemplate::getConditional(const char *pachPlaceholder, size_t cchPlaceholder, bool *pfOutputting)
580{
581#define IS_PLACEHOLDER_MATCH(a_szMatch) \
582 ( cchPlaceholder == sizeof("@@VBOX_COND_" a_szMatch "@@") - 1U \
583 && memcmp(pachPlaceholder, "@@VBOX_COND_" a_szMatch "@@", sizeof("@@VBOX_COND_" a_szMatch "@@") - 1U) == 0)
584
585 /* Install guest additions: */
586 if (IS_PLACEHOLDER_MATCH("IS_INSTALLING_ADDITIONS"))
587 *pfOutputting = mpUnattended->i_getInstallGuestAdditions();
588 else if (IS_PLACEHOLDER_MATCH("IS_NOT_INSTALLING_ADDITIONS"))
589 *pfOutputting = !mpUnattended->i_getInstallGuestAdditions();
590 /* User == Administrator: */
591 else if (IS_PLACEHOLDER_MATCH("IS_USER_LOGIN_ADMINISTRATOR"))
592 *pfOutputting = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) == 0;
593 else if (IS_PLACEHOLDER_MATCH("IS_USER_LOGIN_NOT_ADMINISTRATOR"))
594 *pfOutputting = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) != 0;
595 /* Install TXS: */
596 else if (IS_PLACEHOLDER_MATCH("IS_INSTALLING_TEST_EXEC_SERVICE"))
597 *pfOutputting = mpUnattended->i_getInstallTestExecService();
598 else if (IS_PLACEHOLDER_MATCH("IS_NOT_INSTALLING_TEST_EXEC_SERVICE"))
599 *pfOutputting = !mpUnattended->i_getInstallTestExecService();
600 /* Post install command: */
601 else if (IS_PLACEHOLDER_MATCH("HAS_POST_INSTALL_COMMAND"))
602 *pfOutputting = mpUnattended->i_getPostInstallCommand().isNotEmpty();
603 else if (IS_PLACEHOLDER_MATCH("HAS_NO_POST_INSTALL_COMMAND"))
604 *pfOutputting = !mpUnattended->i_getPostInstallCommand().isNotEmpty();
605 /* Minimal installation: */
606 else if (IS_PLACEHOLDER_MATCH("IS_MINIMAL_INSTALLATION"))
607 *pfOutputting = mpUnattended->i_isMinimalInstallation();
608 else if (IS_PLACEHOLDER_MATCH("IS_NOT_MINIMAL_INSTALLATION"))
609 *pfOutputting = !mpUnattended->i_isMinimalInstallation();
610 /* Is RTC using UTC (i.e. set to UTC time on startup): */
611 else if (IS_PLACEHOLDER_MATCH("IS_RTC_USING_UTC"))
612 *pfOutputting = mpUnattended->i_isRtcUsingUtc();
613 else if (IS_PLACEHOLDER_MATCH("IS_NOT_RTC_USING_UTC"))
614 *pfOutputting = !mpUnattended->i_isRtcUsingUtc();
615 else
616 return mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, mpSetError->tr("Unknown conditional placeholder '%.*s'"),
617 cchPlaceholder, pachPlaceholder);
618 return S_OK;
619#undef IS_PLACEHOLDER_MATCH
620}
621
622
623//////////////////////////////////////////////////////////////////////////////////////////////////////
624/*
625*
626*
627* Implementation GeneralTextScript functions
628*
629*/
630//////////////////////////////////////////////////////////////////////////////////////////////////////
631HRESULT GeneralTextScript::parse()
632{
633 AssertReturn(!mfDataParsed, mpSetError->setErrorBoth(E_FAIL, VERR_WRONG_ORDER, "parse called more than once"));
634
635 /*
636 * Split the raw context into an array of lines.
637 */
638 try
639 {
640 mScriptContentByLines = mStrScriptFullContent.split("\n");
641 }
642 catch (std::bad_alloc)
643 {
644 mScriptContentByLines.clear();
645 return E_OUTOFMEMORY;
646 }
647
648 mfDataParsed = true;
649 return S_OK;
650}
651
652HRESULT GeneralTextScript::saveToString(Utf8Str &rStrDst)
653{
654 AssertReturn(mfDataParsed, mpSetError->setErrorBoth(E_FAIL, VERR_WRONG_ORDER, "saveToString() called before parse()"));
655
656 /*
657 * Calc the required size first.
658 */
659 size_t const cLines = mScriptContentByLines.size();
660 size_t cbTotal = 1;
661 for (size_t iLine = 0; iLine < cLines; iLine++)
662 cbTotal = mScriptContentByLines[iLine].length() + 1;
663
664 /*
665 * Clear the output and try reserve sufficient space.
666 */
667 rStrDst.setNull();
668
669 int vrc = rStrDst.reserveNoThrow(cbTotal);
670 if (RT_FAILURE(vrc))
671 return E_OUTOFMEMORY;
672
673 /*
674 * Assemble the output.
675 */
676 for (size_t iLine = 0; iLine < cLines; iLine++)
677 {
678 try
679 {
680 rStrDst.append(mScriptContentByLines[iLine]);
681 rStrDst.append('\n');
682 }
683 catch (std::bad_alloc)
684 {
685 return E_OUTOFMEMORY;
686 }
687 }
688
689 return S_OK;
690}
691
692const RTCString &GeneralTextScript::getContentOfLine(size_t idxLine)
693{
694 if (idxLine < mScriptContentByLines.size())
695 return mScriptContentByLines[idxLine];
696 return Utf8Str::Empty;
697}
698
699
700HRESULT GeneralTextScript::setContentOfLine(size_t idxLine, const Utf8Str &rStrNewLine)
701{
702 AssertReturn(idxLine < mScriptContentByLines.size(),
703 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE, "attempting to set line %zu when there are only %zu lines",
704 idxLine, mScriptContentByLines.size()));
705 try
706 {
707 mScriptContentByLines[idxLine] = rStrNewLine;
708 }
709 catch (std::bad_alloc)
710 {
711 return E_OUTOFMEMORY;
712 }
713 return S_OK;
714}
715
716vector<size_t> GeneralTextScript::findTemplate(const Utf8Str &rStrNeedle,
717 RTCString::CaseSensitivity enmCase /*= RTCString::CaseSensitive*/)
718{
719 vector<size_t> vecHitLineNumbers;
720 size_t const cLines = mScriptContentByLines.size();
721 for (size_t iLine = 0; iLine < cLines; iLine++)
722 if (mScriptContentByLines[iLine].contains(rStrNeedle, enmCase))
723 vecHitLineNumbers.push_back(iLine);
724
725 return vecHitLineNumbers;
726}
727
728HRESULT GeneralTextScript::findAndReplace(size_t idxLine, const Utf8Str &rStrNeedle, const Utf8Str &rStrReplacement)
729{
730 AssertReturn(idxLine < mScriptContentByLines.size(),
731 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE,
732 "attempting search&replace in line %zu when there are only %zu lines",
733 idxLine, mScriptContentByLines.size()));
734
735 RTCString &rDstString = mScriptContentByLines[idxLine];
736 size_t const offNeedle = rDstString.find(&rStrNeedle);
737 if (offNeedle != RTCString::npos)
738 {
739 try
740 {
741 RTCString strBefore(rDstString, 0, offNeedle);
742 RTCString strAfter(rDstString, offNeedle + rStrNeedle.length());
743 rDstString = strBefore;
744 strBefore.setNull();
745 rDstString.append(rStrReplacement);
746 rDstString.append(strAfter);
747 }
748 catch (std::bad_alloc)
749 {
750 return E_OUTOFMEMORY;
751 }
752 }
753 return S_OK;
754}
755
756HRESULT GeneralTextScript::appendToLine(size_t idxLine, const Utf8Str &rStrToAppend)
757{
758 AssertReturn(idxLine < mScriptContentByLines.size(),
759 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE, "appending to line %zu when there are only %zu lines",
760 idxLine, mScriptContentByLines.size()));
761
762 try
763 {
764 mScriptContentByLines[idxLine].append(rStrToAppend);
765 }
766 catch (std::bad_alloc)
767 {
768 return E_OUTOFMEMORY;
769 }
770 return S_OK;
771}
772
773HRESULT GeneralTextScript::prependToLine(size_t idxLine, const Utf8Str &rStrToPrepend)
774{
775 AssertReturn(idxLine < mScriptContentByLines.size(),
776 mpSetError->setErrorBoth(E_FAIL, VERR_OUT_OF_RANGE, "prepending to line %zu when there are only %zu lines",
777 idxLine, mScriptContentByLines.size()));
778
779 RTCString &rDstString = mScriptContentByLines[idxLine];
780 try
781 {
782 RTCString strCopy;
783 rDstString.swap(strCopy);
784 rDstString.reserve(strCopy.length() + rStrToPrepend.length() + 1);
785 rDstString = rStrToPrepend;
786 rDstString.append(strCopy);
787 }
788 catch (std::bad_alloc)
789 {
790 return E_OUTOFMEMORY;
791 }
792 return S_OK;
793}
794
795#if 0 /* Keeping this a reference */
796//////////////////////////////////////////////////////////////////////////////////////////////////////
797/*
798*
799*
800* Implementation UnattendedSUSEXMLScript functions
801*
802*/
803/////////////////////////////////////////////////////////////////////////////////////////////////////
804HRESULT UnattendedSUSEXMLScript::parse()
805{
806 HRESULT hrc = UnattendedXMLScript::parse();
807 if (SUCCEEDED(hrc))
808 {
809 /*
810 * Check that we've got the right root element type.
811 */
812 const xml::ElementNode *pelmRoot = mDoc.getRootElement();
813 if ( pelmRoot
814 && strcmp(pelmRoot->getName(), "profile") == 0)
815 {
816 /*
817 * Work thought the sections.
818 */
819 try
820 {
821 LoopThruSections(pelmRoot);
822 hrc = S_OK;
823 }
824 catch (std::bad_alloc)
825 {
826 hrc = E_OUTOFMEMORY;
827 }
828 }
829 else if (pelmRoot)
830 hrc = mpSetError->setError(E_FAIL, mpSetError->tr("XML document root element is '%s' instead of 'profile'"),
831 pelmRoot->getName());
832 else
833 hrc = mpSetError->setError(E_FAIL, mpSetError->tr("Missing XML root element"));
834 }
835 return hrc;
836}
837
838HRESULT UnattendedSUSEXMLScript::setFieldInElement(xml::ElementNode *pElement, const DataId enmDataId, const Utf8Str &rStrValue)
839{
840 /*
841 * Don't set empty values.
842 */
843 if (rStrValue.isEmpty())
844 {
845 Utf8Str strProbableValue;
846 try
847 {
848 strProbableValue = createProbableValue(enmDataId, pElement);
849 }
850 catch (std::bad_alloc)
851 {
852 return E_OUTOFMEMORY;
853 }
854 return UnattendedXMLScript::setFieldInElement(pElement, enmDataId, strProbableValue);
855 }
856 return UnattendedXMLScript::setFieldInElement(pElement, enmDataId, rStrValue);
857}
858
859HRESULT UnattendedSUSEXMLScript::LoopThruSections(const xml::ElementNode *pelmRoot)
860{
861 xml::NodesLoop loopChildren(*pelmRoot);
862 const xml::ElementNode *pelmOuterLoop;
863 while ((pelmOuterLoop = loopChildren.forAllNodes()) != NULL)
864 {
865 const char *pcszElemName = pelmOuterLoop->getName();
866 if (!strcmp(pcszElemName, "users"))
867 {
868 xml::NodesLoop loopUsers(*pelmOuterLoop);
869 const xml::ElementNode *pelmUser;
870 while ((pelmUser = loopUsers.forAllNodes()) != NULL)
871 {
872 HRESULT hrc = HandleUserAccountsSection(pelmUser);
873 if (FAILED(hrc))
874 return hrc;
875 }
876 }
877 }
878 return S_OK;
879}
880
881HRESULT UnattendedSUSEXMLScript::HandleUserAccountsSection(const xml::ElementNode *pelmSection)
882{
883 xml::NodesLoop loopUser(*pelmSection);
884
885 const xml::ElementNode *pelmCur;
886 while ((pelmCur = loopUser.forAllNodes()) != NULL)
887 {
888 const char *pszValue = pelmCur->getValue();
889#ifdef LOG_ENABLED
890 if (!RTStrCmp(pelmCur->getName(), "uid"))
891 LogRelFunc(("UnattendedSUSEXMLScript::HandleUserAccountsSection profile/users/%s/%s = %s\n",
892 pelmSection->getName(), pelmCur->getName(), pszValue));
893#endif
894
895 if (!RTStrCmp(pszValue, "$homedir"))
896 mNodesForCorrectionMap.insert(make_pair(USERHOMEDIR_ID, pelmCur));
897
898 if (!RTStrCmp(pszValue, "$user"))
899 mNodesForCorrectionMap.insert(make_pair(USERNAME_ID, pelmCur));
900
901 if (!RTStrCmp(pszValue, "$password"))
902 mNodesForCorrectionMap.insert(make_pair(USERPASSWORD_ID, pelmCur));
903 }
904 return S_OK;
905}
906
907Utf8Str UnattendedSUSEXMLScript::createProbableValue(const DataId enmDataId, const xml::ElementNode *pCurElem)
908{
909 const xml::ElementNode *pElem = pCurElem;
910
911 switch (enmDataId)
912 {
913 case USERHOMEDIR_ID:
914// if ((pElem = pElem->findChildElement("home")))
915// {
916 return createProbableUserHomeDir(pElem);
917// }
918 break;
919 default:
920 break;
921 }
922
923 return Utf8Str::Empty;
924}
925
926Utf8Str UnattendedSUSEXMLScript::createProbableUserHomeDir(const xml::ElementNode *pCurElem)
927{
928 Utf8Str strCalcValue;
929 const xml::ElementNode *pElem = pCurElem->findNextSibilingElement("username");
930 if (pElem)
931 {
932 const char *pszValue = pElem->getValue();
933 strCalcValue = "/home/";
934 strCalcValue.append(pszValue);
935 }
936
937 return strCalcValue;
938}
939#endif /* just for reference */
940
Note: See TracBrowser for help on using the repository browser.

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