VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.2 KB
Line 
1/* $Id: UnattendedScript.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * Classes for reading/parsing/saving scripts for unattended installation.
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 "AutoCaller.h"
36#include <VBox/com/ErrorInfo.h>
37
38#include "UnattendedScript.h"
39#include "UnattendedImpl.h"
40
41#include <iprt/err.h>
42
43#include <iprt/ctype.h>
44#include <iprt/file.h>
45#include <iprt/vfs.h>
46#include <iprt/getopt.h>
47#include <iprt/path.h>
48
49using namespace std;
50
51#ifdef VBOX_WITH_UNATTENDED
52
53
54/*********************************************************************************************************************************
55* Defined Constants And Macros *
56*********************************************************************************************************************************/
57static const char g_szPrefix[] = "@@VBOX_";
58static const char g_szPrefixInsert[] = "@@VBOX_INSERT";
59static const char g_szPrefixInsertXxx[] = "@@VBOX_INSERT_";
60static const char g_szPrefixInsertExpr[] = "@@VBOX_INSERT[";
61static const char g_szPrefixCond[] = "@@VBOX_COND";
62static const char g_szPrefixCondXxx[] = "@@VBOX_COND_";
63static const char g_szPrefixCondExpr[] = "@@VBOX_COND[";
64static const char g_szPrefixCondElse[] = "@@VBOX_COND_ELSE@@";
65static const char g_szPrefixCondEnd[] = "@@VBOX_COND_END@@";
66static const char g_szPrefixSplitter[] = "@@VBOX_SPLITTER";
67
68
69/*********************************************************************************************************************************
70* UnattendedScriptTemplate Implementation *
71*********************************************************************************************************************************/
72
73UnattendedScriptTemplate::UnattendedScriptTemplate(Unattended *pUnattended, const char *pszDefaultTemplateFilename,
74 const char *pszDefaultFilename)
75 : BaseTextScript(pUnattended, pszDefaultTemplateFilename, pszDefaultFilename), mpUnattended(pUnattended)
76{
77}
78
79HRESULT UnattendedScriptTemplate::saveToString(Utf8Str &rStrDst)
80{
81 RTEXPREVAL hEvaluator = NIL_RTEXPREVAL;
82 int vrc = RTExprEvalCreate(&hEvaluator, 0, "unattended", this, UnattendedScriptTemplate::queryVariableForExpr);
83 AssertRCReturn(vrc, mpSetError->setErrorVrc(vrc));
84
85 struct
86 {
87 bool fSavedOutputting;
88 } aConds[8];
89 unsigned cConds = 0;
90 bool fOutputting = true;
91 HRESULT hrc = E_FAIL;
92 size_t offTemplate = 0;
93 size_t cchTemplate = mStrScriptFullContent.length();
94 rStrDst.setNull();
95 for (;;)
96 {
97 /*
98 * Find the next placeholder and add any text before it to the output.
99 */
100 size_t offPlaceholder = mStrScriptFullContent.find(g_szPrefix, offTemplate);
101 size_t cchToCopy = offPlaceholder != RTCString::npos ? offPlaceholder - offTemplate : cchTemplate - offTemplate;
102 if (cchToCopy > 0)
103 {
104 if (fOutputting)
105 {
106 try
107 {
108 rStrDst.append(mStrScriptFullContent, offTemplate , cchToCopy);
109 }
110 catch (std::bad_alloc &)
111 {
112 hrc = E_OUTOFMEMORY;
113 break;
114 }
115 }
116 offTemplate += cchToCopy;
117 }
118
119 /*
120 * Process placeholder.
121 */
122 if (offPlaceholder != RTCString::npos)
123 {
124 /*
125 * First we must find the end of the placeholder string.
126 */
127 size_t const cchMaxPlaceholder = RT_MIN(cchTemplate - offPlaceholder, _1K);
128 const char *pszPlaceholder = mStrScriptFullContent.c_str() + offPlaceholder;
129 size_t cchPlaceholder = sizeof(g_szPrefix) - 1;
130 char ch;
131 while ( cchPlaceholder < cchMaxPlaceholder
132 && (ch = pszPlaceholder[cchPlaceholder]) != '\0'
133 && (RT_C_IS_PRINT(ch) || RT_C_IS_SPACE(ch))
134 && ch != '@')
135 cchPlaceholder++;
136
137 if ( offPlaceholder + cchPlaceholder < cchTemplate
138 && pszPlaceholder[cchPlaceholder] == '@')
139 {
140 cchPlaceholder++;
141 if ( offPlaceholder + cchPlaceholder < cchTemplate
142 && pszPlaceholder[cchPlaceholder] == '@')
143 cchPlaceholder++;
144 }
145
146 if ( pszPlaceholder[cchPlaceholder - 1] != '@'
147 || pszPlaceholder[cchPlaceholder - 2] != '@'
148 || ( strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixInsert)) != 0
149 && strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixCond)) != 0
150 && strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixSplitter)) != 0 ) )
151 {
152 hrc = mpSetError->setError(E_FAIL, tr("Malformed or too long template placeholder '%.*s'"),
153 cchPlaceholder, pszPlaceholder);
154 break;
155 }
156
157 offTemplate += cchPlaceholder;
158
159 /*
160 * @@VBOX_INSERT_XXX@@:
161 */
162 if (strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixInsertXxx)) == 0)
163 {
164 /*
165 * Get the placeholder value and add it to the output.
166 */
167 RTCString strValue;
168 hrc = getReplacement(pszPlaceholder, cchPlaceholder, fOutputting, strValue);
169 if (SUCCEEDED(hrc))
170 {
171 if (fOutputting)
172 {
173 try
174 {
175 rStrDst.append(strValue);
176 }
177 catch (std::bad_alloc &)
178 {
179 hrc = E_OUTOFMEMORY;
180 break;
181 }
182 }
183 }
184 else
185 break;
186 }
187 /*
188 * @@VBOX_INSERT[expr]@@:
189 * @@VBOX_INSERT[expr]SH@@:
190 * @@VBOX_INSERT[expr]ELEMENT@@:
191 * @@VBOX_INSERT[expr]ATTRIB_DQ@@:
192 */
193 else if (strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixInsertExpr)) == 0)
194 {
195 /*
196 * Get the placeholder value and add it to the output.
197 */
198 char *pszValue = NULL;
199 hrc = getReplacementForExpr(hEvaluator, pszPlaceholder, cchPlaceholder, fOutputting, &pszValue);
200 if (SUCCEEDED(hrc))
201 {
202 if (fOutputting && pszValue)
203 {
204 try
205 {
206 rStrDst.append(pszValue);
207 }
208 catch (std::bad_alloc &)
209 {
210 hrc = E_OUTOFMEMORY;
211 break;
212 }
213 }
214 RTStrFree(pszValue);
215 }
216 else
217 break;
218 }
219 /*
220 * @@VBOX_COND_END@@: Pop one item of the conditional stack.
221 */
222 else if (strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixCondEnd)) == 0)
223 {
224 if (cConds > 0)
225 {
226 cConds--;
227 fOutputting = aConds[cConds].fSavedOutputting;
228 }
229 else
230 {
231 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
232 tr("%s without @@VBOX_COND_XXX@@ at offset %zu (%#zx)"),
233 g_szPrefixCondEnd, offPlaceholder, offPlaceholder);
234 break;
235 }
236 }
237 /*
238 * @@VBOX_COND_ELSE@@: Flip the output setting of the current condition.
239 */
240 else if (strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixCondElse)) == 0)
241 {
242 if (cConds > 0)
243 fOutputting = !fOutputting;
244 else
245 {
246 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
247 tr("%s without @@VBOX_COND_XXX@@ at offset %zu (%#zx)"),
248 g_szPrefixCondElse, offPlaceholder, offPlaceholder);
249 break;
250 }
251 }
252 /*
253 * @@VBOX_COND_XXX@@: Push the previous outputting state and combine it with the
254 * one from the condition.
255 */
256 else if (strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixCondXxx)) == 0)
257 {
258 if (cConds + 1 < RT_ELEMENTS(aConds))
259 {
260 aConds[cConds].fSavedOutputting = fOutputting;
261 bool fNewOutputting = fOutputting;
262 hrc = getConditional(pszPlaceholder, cchPlaceholder, &fNewOutputting);
263 if (SUCCEEDED(hrc))
264 fOutputting = fOutputting && fNewOutputting;
265 else
266 break;
267 cConds++;
268 }
269 else
270 {
271 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
272 tr("Too deep conditional nesting at offset %zu (%#zx)"),
273 offPlaceholder, offPlaceholder);
274 break;
275 }
276 }
277 /*
278 * @@VBOX_COND[expr]@@: Push the previous outputting state and combine it with the
279 * one from the condition.
280 */
281 else if (strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixCondExpr)) == 0)
282 {
283 if (cConds + 1 < RT_ELEMENTS(aConds))
284 {
285 aConds[cConds].fSavedOutputting = fOutputting;
286 bool fNewOutputting = fOutputting;
287 hrc = resolveConditionalExpr(hEvaluator, pszPlaceholder, cchPlaceholder, &fNewOutputting);
288 if (SUCCEEDED(hrc))
289 fOutputting = fOutputting && fNewOutputting;
290 else
291 break;
292 cConds++;
293 }
294 else
295 {
296 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
297 tr("Too deep conditional nesting at offset %zu (%#zx)"),
298 offPlaceholder, offPlaceholder);
299 break;
300 }
301 }
302 /*
303 * @@VBOX_SPLITTER_START/END[filename]@@: Ignored in this pass.
304 */
305 else
306 {
307 Assert(strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixSplitter)) == 0);
308 if (fOutputting)
309 {
310 try
311 {
312 rStrDst.append(pszPlaceholder, cchPlaceholder);
313 }
314 catch (std::bad_alloc &)
315 {
316 hrc = E_OUTOFMEMORY;
317 break;
318 }
319 }
320 }
321 }
322
323 /*
324 * Done?
325 */
326 if (offTemplate >= cchTemplate)
327 {
328 if (cConds == 0)
329 {
330 RTExprEvalRelease(hEvaluator);
331 return S_OK;
332 }
333 if (cConds == 1)
334 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, tr("Missing @@VBOX_COND_END@@"));
335 else
336 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, tr("Missing %u @@VBOX_COND_END@@"), cConds);
337 break;
338 }
339 }
340
341 /* failed */
342 rStrDst.setNull();
343 RTExprEvalRelease(hEvaluator);
344 return hrc;
345}
346
347HRESULT UnattendedScriptTemplate::getReplacement(const char *pachPlaceholder, size_t cchPlaceholder,
348 bool fOutputting, RTCString &rValue)
349{
350 /*
351 * Check for an escaping suffix. Drop the '@@'.
352 */
353 kEvalEscaping_T enmEscaping;
354#define PLACEHOLDER_ENDS_WITH(a_szSuffix) \
355 ( cchPlaceholder > sizeof(a_szSuffix) - 1U \
356 && memcmp(&pachPlaceholder[cchPlaceholder - sizeof(a_szSuffix) + 1U], a_szSuffix, sizeof(a_szSuffix) - 1U) == 0)
357 if (PLACEHOLDER_ENDS_WITH("_SH@@"))
358 {
359 cchPlaceholder -= 3 + 2;
360 enmEscaping = kValueEscaping_Bourne;
361 }
362 else if (PLACEHOLDER_ENDS_WITH("_ELEMENT@@"))
363 {
364 cchPlaceholder -= 8 + 2;
365 enmEscaping = kValueEscaping_XML_Element;
366 }
367 else if (PLACEHOLDER_ENDS_WITH("_ATTRIB_DQ@@"))
368 {
369 cchPlaceholder -= 10 + 2;
370 enmEscaping = kValueEscaping_XML_Attribute_Double_Quotes;
371 }
372 else
373 {
374 Assert(PLACEHOLDER_ENDS_WITH("@@"));
375 cchPlaceholder -= 2;
376 enmEscaping = kValueEscaping_None;
377 }
378#undef PLACEHOLDER_ENDS_WITH
379
380 /*
381 * Resolve and escape the value.
382 */
383 HRESULT hrc;
384 try
385 {
386 Utf8Str strTmp;
387 const char *pszReadOnlyValue = NULL;
388 int vrc = queryVariable(pachPlaceholder + sizeof(g_szPrefixInsertXxx) - 1,
389 cchPlaceholder - sizeof(g_szPrefixInsertXxx) + 1,
390 strTmp, fOutputting ? &pszReadOnlyValue : NULL);
391 if (RT_SUCCESS(vrc))
392 {
393 if (fOutputting)
394 {
395 Assert(pszReadOnlyValue != NULL);
396 switch (enmEscaping)
397 {
398 case kValueEscaping_None:
399 rValue = pszReadOnlyValue;
400 return S_OK;
401
402 case kValueEscaping_Bourne:
403 case kValueEscaping_XML_Element:
404 case kValueEscaping_XML_Attribute_Double_Quotes:
405 {
406 switch (enmEscaping)
407 {
408 case kValueEscaping_Bourne:
409 {
410 const char * const papszArgs[2] = { pszReadOnlyValue, NULL };
411 char *pszEscaped = NULL;
412 vrc = RTGetOptArgvToString(&pszEscaped, papszArgs, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
413 if (RT_SUCCESS(vrc))
414 {
415 try
416 {
417 rValue = pszEscaped;
418 RTStrFree(pszEscaped);
419 return S_OK;
420 }
421 catch (std::bad_alloc &)
422 {
423 hrc = E_OUTOFMEMORY;
424 }
425 RTStrFree(pszEscaped);
426 }
427 else
428 hrc = mpSetError->setErrorVrc(vrc);
429 break;
430 }
431
432 case kValueEscaping_XML_Element:
433 rValue.printf("%RMes", pszReadOnlyValue);
434 return S_OK;
435
436 case kValueEscaping_XML_Attribute_Double_Quotes:
437 {
438 RTCString strTmp2;
439 strTmp2.printf("%RMas", pszReadOnlyValue);
440 rValue = RTCString(strTmp2, 1, strTmp2.length() - 2);
441 return S_OK;
442 }
443
444 default:
445 hrc = E_FAIL;
446 break;
447 }
448 break;
449 }
450
451 default:
452 AssertFailedStmt(hrc = E_FAIL);
453 break;
454 }
455 }
456 else
457 hrc = S_OK;
458 }
459 else
460 hrc = E_FAIL;
461 }
462 catch (std::bad_alloc &)
463 {
464 hrc = E_OUTOFMEMORY;
465 }
466 rValue.setNull();
467 return hrc;
468}
469
470HRESULT UnattendedScriptTemplate::getReplacementForExpr(RTEXPREVAL hEvaluator, const char *pachPlaceholder, size_t cchPlaceholder,
471 bool fOutputting, char **ppszValue) RT_NOEXCEPT
472{
473 /*
474 * Process the tail of the placeholder to figure out the escaping rules.
475 *
476 * @@VBOX_INSERT[expr]@@:
477 * @@VBOX_INSERT[expr]SH@@:
478 * @@VBOX_INSERT[expr]ELEMENT@@:
479 * @@VBOX_INSERT[expr]ATTRIB_DQ@@:
480 */
481 kEvalEscaping_T enmEscaping;
482#define PLACEHOLDER_ENDS_WITH(a_szSuffix) \
483 ( cchPlaceholder > sizeof(a_szSuffix) - 1U \
484 && memcmp(&pachPlaceholder[cchPlaceholder - sizeof(a_szSuffix) + 1U], a_szSuffix, sizeof(a_szSuffix) - 1U) == 0)
485 if (PLACEHOLDER_ENDS_WITH("]SH@@"))
486 {
487 cchPlaceholder -= sizeof("]SH@@") - 1;
488 enmEscaping = kValueEscaping_Bourne;
489 }
490 else if (PLACEHOLDER_ENDS_WITH("]ELEMENT@@"))
491 {
492 cchPlaceholder -= sizeof("]ELEMENT@@") - 1;
493 enmEscaping = kValueEscaping_XML_Element;
494 }
495 else if (PLACEHOLDER_ENDS_WITH("]ATTRIB_DQ@@"))
496 {
497 cchPlaceholder -= sizeof("]ATTRIB_DQ@@") - 1;
498 enmEscaping = kValueEscaping_XML_Attribute_Double_Quotes;
499 }
500 else if (PLACEHOLDER_ENDS_WITH("]@@"))
501 {
502 cchPlaceholder -= sizeof("]@@") - 1;
503 enmEscaping = kValueEscaping_None;
504 }
505 else
506 return mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, tr("Malformed @@VBOX_INSERT[expr]@@: Missing ']' (%.*s)"),
507 cchPlaceholder, pachPlaceholder);
508#undef PLACEHOLDER_ENDS_WITH
509
510 /* The placeholder prefix length. The expression is from cchPrefix to cchPlaceholder. */
511 size_t const cchPrefix = sizeof(g_szPrefixInsertExpr) - 1;
512 Assert(pachPlaceholder[cchPrefix - 1] == '[');
513
514 /*
515 * Evaluate the expression. We do this regardless of fOutput for now.
516 */
517 RTERRINFOSTATIC ErrInfo;
518 char *pszValue = NULL;
519 int vrc = RTExprEvalToString(hEvaluator, &pachPlaceholder[cchPrefix], cchPlaceholder - cchPrefix, &pszValue,
520 RTErrInfoInitStatic(&ErrInfo));
521 LogFlowFunc(("RTExprEvalToString(%.*s) -> %Rrc pszValue=%s\n",
522 cchPlaceholder - cchPrefix, &pachPlaceholder[cchPrefix], vrc, pszValue));
523 if (RT_SUCCESS(vrc))
524 {
525 if (fOutputting)
526 {
527 switch (enmEscaping)
528 {
529 case kValueEscaping_None:
530 *ppszValue = pszValue;
531 pszValue = NULL;
532 break;
533
534 case kValueEscaping_Bourne:
535 {
536 const char * const papszArgs[2] = { pszValue, NULL };
537 vrc = RTGetOptArgvToString(ppszValue, papszArgs, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
538 break;
539 }
540
541 case kValueEscaping_XML_Element:
542 vrc = RTStrAPrintf(ppszValue, "%RMes", pszValue);
543 break;
544
545 case kValueEscaping_XML_Attribute_Double_Quotes:
546 vrc = RTStrAPrintf(ppszValue, "%RMas", pszValue);
547 if (RT_SUCCESS(vrc))
548 {
549 /* drop the quotes */
550 char *pszRet = *ppszValue;
551 size_t const cchRet = strlen(pszRet) - 2;
552 memmove(pszRet, &pszRet[1], cchRet);
553 pszRet[cchRet] = '\0';
554 }
555 break;
556
557 default:
558 AssertFailedStmt(vrc = VERR_IPE_NOT_REACHED_DEFAULT_CASE);
559 break;
560 }
561 RTStrFree(pszValue);
562 if (RT_FAILURE(vrc))
563 return mpSetError->setErrorVrc(vrc);
564 }
565 else
566 {
567 *ppszValue = NULL;
568 RTStrFree(pszValue);
569 }
570 }
571 else
572 return mpSetError->setErrorBoth(E_FAIL, vrc, tr("Expression evaluation error for '%.*s': %#RTeic"),
573 cchPlaceholder, pachPlaceholder, &ErrInfo.Core);
574 return S_OK;
575}
576
577HRESULT UnattendedScriptTemplate::resolveConditionalExpr(RTEXPREVAL hEvaluator, const char *pachPlaceholder,
578 size_t cchPlaceholder, bool *pfOutputting) RT_NOEXCEPT
579{
580 /*
581 * Check the placeholder tail: @@VBOX_COND[expr]@@
582 */
583 static const char s_szTail[] = "]@@";
584 if (memcmp(&pachPlaceholder[cchPlaceholder - sizeof(s_szTail) + 1], RT_STR_TUPLE(s_szTail)) != 0)
585 return mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, tr("Malformed @@VBOX_COND[expr]@@: Missing ']' (%.*s)"),
586 cchPlaceholder, pachPlaceholder);
587 Assert(pachPlaceholder[sizeof(g_szPrefixCondExpr) - 2 ] == '[');
588
589 /*
590 * Evaluate the expression.
591 */
592 RTERRINFOSTATIC ErrInfo;
593 const char * const pchExpr = &pachPlaceholder[sizeof(g_szPrefixCondExpr) - 1];
594 size_t const cchExpr = cchPlaceholder - sizeof(g_szPrefixCondExpr) + 1 - sizeof(s_szTail) + 1;
595 int vrc = RTExprEvalToBool(hEvaluator, pchExpr, cchExpr, pfOutputting, RTErrInfoInitStatic(&ErrInfo));
596 LogFlowFunc(("RTExprEvalToBool(%.*s) -> %Rrc *pfOutputting=%s\n", cchExpr, pchExpr, vrc, *pfOutputting));
597 if (RT_SUCCESS(vrc))
598 return S_OK;
599 return mpSetError->setErrorBoth(E_FAIL, vrc, tr("Expression evaluation error for '%.*s': %#RTeic"),
600 cchPlaceholder, pachPlaceholder, &ErrInfo.Core);
601}
602
603/*static */ DECLCALLBACK(int)
604UnattendedScriptTemplate::queryVariableForExpr(const char *pchName, size_t cchName, void *pvUser, char **ppszValue) RT_NOEXCEPT
605{
606 UnattendedScriptTemplate *pThis = (UnattendedScriptTemplate *)pvUser;
607 int vrc;
608 try
609 {
610 const char *pszReadOnlyValue = NULL;
611 Utf8Str strTmp;
612 vrc = pThis->queryVariable(pchName, cchName, strTmp, ppszValue ? &pszReadOnlyValue : NULL);
613 if (ppszValue)
614 {
615 if (RT_SUCCESS(vrc))
616 vrc = RTStrDupEx(ppszValue, pszReadOnlyValue);
617 else
618 *ppszValue = NULL;
619 }
620 }
621 catch (std::bad_alloc &)
622 {
623 vrc = VERR_NO_MEMORY;
624 *ppszValue = NULL;
625 }
626 return vrc;
627}
628
629int UnattendedScriptTemplate::queryVariable(const char *pchName, size_t cchName, Utf8Str &rstrTmp, const char **ppszValue)
630{
631#define IS_MATCH(a_szMatch) \
632 (cchName == sizeof(a_szMatch) - 1U && memcmp(pchName, a_szMatch, sizeof(a_szMatch) - 1U) == 0)
633
634 const char *pszValue;
635
636 /*
637 * Variables
638 */
639 if (IS_MATCH("USER_LOGIN"))
640 pszValue = mpUnattended->i_getUser().c_str();
641 else if (IS_MATCH("USER_PASSWORD"))
642 pszValue = mpUnattended->i_getPassword().c_str();
643 else if (IS_MATCH("ROOT_PASSWORD"))
644 pszValue = mpUnattended->i_getPassword().c_str();
645 else if (IS_MATCH("USER_FULL_NAME"))
646 pszValue = mpUnattended->i_getFullUserName().c_str();
647 else if (IS_MATCH("PRODUCT_KEY"))
648 pszValue = mpUnattended->i_getProductKey().c_str();
649 else if (IS_MATCH("POST_INSTALL_COMMAND"))
650 pszValue = mpUnattended->i_getPostInstallCommand().c_str();
651 else if (IS_MATCH("AUXILIARY_INSTALL_DIR"))
652 pszValue = mpUnattended->i_getAuxiliaryInstallDir().c_str();
653 else if (IS_MATCH("IMAGE_INDEX"))
654 pszValue = rstrTmp.printf("%u", mpUnattended->i_getImageIndex()).c_str();
655 else if (IS_MATCH("OS_ARCH"))
656 pszValue = mpUnattended->i_isGuestOs64Bit() ? "amd64" : "x86";
657 else if (IS_MATCH("OS_ARCH2"))
658 pszValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "x86";
659 else if (IS_MATCH("OS_ARCH3"))
660 pszValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i386";
661 else if (IS_MATCH("OS_ARCH4"))
662 pszValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i486";
663 else if (IS_MATCH("OS_ARCH6"))
664 pszValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i686";
665 else if (IS_MATCH("GUEST_OS_VERSION"))
666 pszValue = mpUnattended->i_getDetectedOSVersion().c_str();
667 else if (IS_MATCH("GUEST_OS_MAJOR_VERSION"))
668 {
669 Utf8Str const &rstrOsVer = mpUnattended->i_getDetectedOSVersion();
670 size_t offDot = rstrOsVer.find('.');
671 if (offDot > 0 && offDot != Utf8Str::npos)
672 pszValue = rstrTmp.assign(rstrOsVer, 0, offDot).c_str(); /* caller catches std::bad_alloc */
673 else if (!ppszValue)
674 return VERR_NOT_FOUND;
675 else
676 {
677 mpSetError->setErrorBoth(E_FAIL, VERR_NO_DATA, tr("Unknown guest OS major version '%s'"), rstrOsVer.c_str());
678 return VERR_NO_DATA;
679 }
680 }
681 else if (IS_MATCH("TIME_ZONE_UX"))
682 pszValue = mpUnattended->i_getTimeZoneInfo()
683 ? mpUnattended->i_getTimeZoneInfo()->pszUnixName : mpUnattended->i_getTimeZone().c_str();
684 else if (IS_MATCH("TIME_ZONE_WIN_NAME"))
685 {
686 PCRTTIMEZONEINFO pInfo = mpUnattended->i_getTimeZoneInfo();
687 if (pInfo)
688 pszValue = pInfo->pszWindowsName ? pInfo->pszWindowsName : "GMT";
689 else
690 pszValue = mpUnattended->i_getTimeZone().c_str();
691 }
692 else if (IS_MATCH("TIME_ZONE_WIN_INDEX"))
693 {
694 PCRTTIMEZONEINFO pInfo = mpUnattended->i_getTimeZoneInfo();
695 if (pInfo)
696 pszValue = rstrTmp.printf("%u", pInfo->idxWindows ? pInfo->idxWindows : 85 /*GMT*/).c_str();
697 else
698 pszValue = mpUnattended->i_getTimeZone().c_str();
699 }
700 else if (IS_MATCH("LOCALE"))
701 pszValue = mpUnattended->i_getLocale().c_str();
702 else if (IS_MATCH("DASH_LOCALE"))
703 {
704 Assert(mpUnattended->i_getLocale()[2] == '_');
705 pszValue = rstrTmp.assign(mpUnattended->i_getLocale()).replace(2, 1, "-").c_str();
706 }
707 else if (IS_MATCH("LANGUAGE"))
708 pszValue = mpUnattended->i_getLanguage().c_str();
709 else if (IS_MATCH("COUNTRY"))
710 pszValue = mpUnattended->i_getCountry().c_str();
711 else if (IS_MATCH("HOSTNAME_FQDN"))
712 pszValue = mpUnattended->i_getHostname().c_str();
713 else if (IS_MATCH("HOSTNAME_WITHOUT_DOMAIN"))
714 pszValue = rstrTmp.assign(mpUnattended->i_getHostname(), 0, mpUnattended->i_getHostname().find(".")).c_str();
715 else if (IS_MATCH("HOSTNAME_WITHOUT_DOMAIN_MAX_15"))
716 pszValue = rstrTmp.assign(mpUnattended->i_getHostname(), 0, RT_MIN(mpUnattended->i_getHostname().find("."), 15)).c_str();
717 else if (IS_MATCH("HOSTNAME_DOMAIN"))
718 pszValue = rstrTmp.assign(mpUnattended->i_getHostname(), mpUnattended->i_getHostname().find(".") + 1).c_str();
719 else if (IS_MATCH("PROXY"))
720 pszValue = mpUnattended->i_getProxy().c_str();
721 /*
722 * Indicator variables.
723 */
724 else if (IS_MATCH("IS_INSTALLING_ADDITIONS"))
725 pszValue = mpUnattended->i_getInstallGuestAdditions() ? "1" : "0";
726 else if (IS_MATCH("IS_USER_LOGIN_ADMINISTRATOR"))
727 pszValue = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) == 0 ? "1" : "0";
728 else if (IS_MATCH("IS_INSTALLING_TEST_EXEC_SERVICE"))
729 pszValue = mpUnattended->i_getInstallTestExecService() ? "1" : "0";
730 else if (IS_MATCH("HAS_POST_INSTALL_COMMAND"))
731 pszValue = mpUnattended->i_getPostInstallCommand().isNotEmpty() ? "1" : "0";
732 else if (IS_MATCH("HAS_PRODUCT_KEY"))
733 pszValue = mpUnattended->i_getProductKey().isNotEmpty() ? "1" : "0";
734 else if (IS_MATCH("IS_MINIMAL_INSTALLATION"))
735 pszValue = mpUnattended->i_isMinimalInstallation() ? "1" : "0";
736 else if (IS_MATCH("IS_FIRMWARE_UEFI"))
737 pszValue = mpUnattended->i_isFirmwareEFI() ? "1" : "0";
738 else if (IS_MATCH("IS_RTC_USING_UTC"))
739 pszValue = mpUnattended->i_isRtcUsingUtc() ? "1" : "0";
740 else if (IS_MATCH("HAS_PROXY"))
741 pszValue = mpUnattended->i_getProxy().isNotEmpty() ? "1" : "0";
742 /*
743 * Unknown variable.
744 */
745 else if (!ppszValue)
746 return VERR_NOT_FOUND;
747 else
748 {
749 mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, tr("Unknown variable '%.*s'"), cchName, pchName);
750 return VERR_NO_DATA;
751 }
752 if (ppszValue)
753 *ppszValue = pszValue;
754 return VINF_SUCCESS;
755}
756
757HRESULT UnattendedScriptTemplate::getConditional(const char *pachPlaceholder, size_t cchPlaceholder, bool *pfOutputting)
758{
759#define IS_PLACEHOLDER_MATCH(a_szMatch) \
760 ( cchPlaceholder == sizeof("@@VBOX_COND_" a_szMatch "@@") - 1U \
761 && memcmp(pachPlaceholder, "@@VBOX_COND_" a_szMatch "@@", sizeof("@@VBOX_COND_" a_szMatch "@@") - 1U) == 0)
762
763 /* Install Guest Additions: */
764 if (IS_PLACEHOLDER_MATCH("IS_INSTALLING_ADDITIONS"))
765 *pfOutputting = mpUnattended->i_getInstallGuestAdditions();
766 else if (IS_PLACEHOLDER_MATCH("IS_NOT_INSTALLING_ADDITIONS"))
767 *pfOutputting = !mpUnattended->i_getInstallGuestAdditions();
768 /* User == Administrator: */
769 else if (IS_PLACEHOLDER_MATCH("IS_USER_LOGIN_ADMINISTRATOR"))
770 *pfOutputting = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) == 0;
771 else if (IS_PLACEHOLDER_MATCH("IS_USER_LOGIN_NOT_ADMINISTRATOR"))
772 *pfOutputting = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) != 0;
773 /* Install TXS: */
774 else if (IS_PLACEHOLDER_MATCH("IS_INSTALLING_TEST_EXEC_SERVICE"))
775 *pfOutputting = mpUnattended->i_getInstallTestExecService();
776 else if (IS_PLACEHOLDER_MATCH("IS_NOT_INSTALLING_TEST_EXEC_SERVICE"))
777 *pfOutputting = !mpUnattended->i_getInstallTestExecService();
778 /* Post install command: */
779 else if (IS_PLACEHOLDER_MATCH("HAS_POST_INSTALL_COMMAND"))
780 *pfOutputting = mpUnattended->i_getPostInstallCommand().isNotEmpty();
781 else if (IS_PLACEHOLDER_MATCH("HAS_NO_POST_INSTALL_COMMAND"))
782 *pfOutputting = mpUnattended->i_getPostInstallCommand().isEmpty();
783 /* Product key: */
784 else if (IS_PLACEHOLDER_MATCH("HAS_PRODUCT_KEY"))
785 *pfOutputting = mpUnattended->i_getProductKey().isNotEmpty();
786 else if (IS_PLACEHOLDER_MATCH("HAS_NO_PRODUCT_KEY"))
787 *pfOutputting = mpUnattended->i_getProductKey().isEmpty();
788 /* Minimal installation: */
789 else if (IS_PLACEHOLDER_MATCH("IS_MINIMAL_INSTALLATION"))
790 *pfOutputting = mpUnattended->i_isMinimalInstallation();
791 else if (IS_PLACEHOLDER_MATCH("IS_NOT_MINIMAL_INSTALLATION"))
792 *pfOutputting = !mpUnattended->i_isMinimalInstallation();
793 /* Is firmware UEFI: */
794 else if (IS_PLACEHOLDER_MATCH("IS_FIRMWARE_UEFI"))
795 *pfOutputting = mpUnattended->i_isFirmwareEFI();
796 else if (IS_PLACEHOLDER_MATCH("IS_NOT_FIRMWARE_UEFI"))
797 *pfOutputting = !mpUnattended->i_isFirmwareEFI();
798 /* Is RTC using UTC (i.e. set to UTC time on startup): */
799 else if (IS_PLACEHOLDER_MATCH("IS_RTC_USING_UTC"))
800 *pfOutputting = mpUnattended->i_isRtcUsingUtc();
801 else if (IS_PLACEHOLDER_MATCH("IS_NOT_RTC_USING_UTC"))
802 *pfOutputting = !mpUnattended->i_isRtcUsingUtc();
803 else if (IS_PLACEHOLDER_MATCH("HAS_PROXY"))
804 *pfOutputting = mpUnattended->i_getProxy().isNotEmpty();
805 else if (IS_PLACEHOLDER_MATCH("AVOID_UPDATES_OVER_NETWORK"))
806 *pfOutputting = mpUnattended->i_getAvoidUpdatesOverNetwork();
807 else
808 return mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, tr("Unknown conditional placeholder '%.*s'"),
809 cchPlaceholder, pachPlaceholder);
810 return S_OK;
811#undef IS_PLACEHOLDER_MATCH
812}
813
814#endif /* VBOX_WITH_UNATTENDED */
815#if 0 /* Keeping this a reference */
816
817
818/*********************************************************************************************************************************
819* UnattendedSUSEXMLScript Implementation *
820*********************************************************************************************************************************/
821
822HRESULT UnattendedSUSEXMLScript::parse()
823{
824 HRESULT hrc = UnattendedXMLScript::parse();
825 if (SUCCEEDED(hrc))
826 {
827 /*
828 * Check that we've got the right root element type.
829 */
830 const xml::ElementNode *pelmRoot = mDoc.getRootElement();
831 if ( pelmRoot
832 && strcmp(pelmRoot->getName(), "profile") == 0)
833 {
834 /*
835 * Work thought the sections.
836 */
837 try
838 {
839 LoopThruSections(pelmRoot);
840 hrc = S_OK;
841 }
842 catch (std::bad_alloc &)
843 {
844 hrc = E_OUTOFMEMORY;
845 }
846 }
847 else if (pelmRoot)
848 hrc = mpSetError->setError(E_FAIL, tr("XML document root element is '%s' instead of 'profile'"),
849 pelmRoot->getName());
850 else
851 hrc = mpSetError->setError(E_FAIL, tr("Missing XML root element"));
852 }
853 return hrc;
854}
855
856HRESULT UnattendedSUSEXMLScript::setFieldInElement(xml::ElementNode *pElement, const DataId enmDataId, const Utf8Str &rStrValue)
857{
858 /*
859 * Don't set empty values.
860 */
861 if (rStrValue.isEmpty())
862 {
863 Utf8Str strProbableValue;
864 try
865 {
866 strProbableValue = createProbableValue(enmDataId, pElement);
867 }
868 catch (std::bad_alloc &)
869 {
870 return E_OUTOFMEMORY;
871 }
872 return UnattendedXMLScript::setFieldInElement(pElement, enmDataId, strProbableValue);
873 }
874 return UnattendedXMLScript::setFieldInElement(pElement, enmDataId, rStrValue);
875}
876
877HRESULT UnattendedSUSEXMLScript::LoopThruSections(const xml::ElementNode *pelmRoot)
878{
879 xml::NodesLoop loopChildren(*pelmRoot);
880 const xml::ElementNode *pelmOuterLoop;
881 while ((pelmOuterLoop = loopChildren.forAllNodes()) != NULL)
882 {
883 const char *pcszElemName = pelmOuterLoop->getName();
884 if (!strcmp(pcszElemName, "users"))
885 {
886 xml::NodesLoop loopUsers(*pelmOuterLoop);
887 const xml::ElementNode *pelmUser;
888 while ((pelmUser = loopUsers.forAllNodes()) != NULL)
889 {
890 HRESULT hrc = HandleUserAccountsSection(pelmUser);
891 if (FAILED(hrc))
892 return hrc;
893 }
894 }
895 }
896 return S_OK;
897}
898
899HRESULT UnattendedSUSEXMLScript::HandleUserAccountsSection(const xml::ElementNode *pelmSection)
900{
901 xml::NodesLoop loopUser(*pelmSection);
902
903 const xml::ElementNode *pelmCur;
904 while ((pelmCur = loopUser.forAllNodes()) != NULL)
905 {
906 const char *pszValue = pelmCur->getValue();
907#ifdef LOG_ENABLED
908 if (!RTStrCmp(pelmCur->getName(), "uid"))
909 LogRelFunc(("UnattendedSUSEXMLScript::HandleUserAccountsSection profile/users/%s/%s = %s\n",
910 pelmSection->getName(), pelmCur->getName(), pszValue));
911#endif
912
913 if (!RTStrCmp(pszValue, "$homedir"))
914 mNodesForCorrectionMap.insert(make_pair(USERHOMEDIR_ID, pelmCur));
915
916 if (!RTStrCmp(pszValue, "$user"))
917 mNodesForCorrectionMap.insert(make_pair(USERNAME_ID, pelmCur));
918
919 if (!RTStrCmp(pszValue, "$password"))
920 mNodesForCorrectionMap.insert(make_pair(USERPASSWORD_ID, pelmCur));
921 }
922 return S_OK;
923}
924
925Utf8Str UnattendedSUSEXMLScript::createProbableValue(const DataId enmDataId, const xml::ElementNode *pCurElem)
926{
927 const xml::ElementNode *pElem = pCurElem;
928
929 switch (enmDataId)
930 {
931 case USERHOMEDIR_ID:
932// if ((pElem = pElem->findChildElement("home")))
933// {
934 return createProbableUserHomeDir(pElem);
935// }
936 break;
937 default:
938 break;
939 }
940
941 return Utf8Str::Empty;
942}
943
944Utf8Str UnattendedSUSEXMLScript::createProbableUserHomeDir(const xml::ElementNode *pCurElem)
945{
946 Utf8Str strCalcValue;
947 const xml::ElementNode *pElem = pCurElem->findNextSibilingElement("username");
948 if (pElem)
949 {
950 const char *pszValue = pElem->getValue();
951 strCalcValue = "/home/";
952 strCalcValue.append(pszValue);
953 }
954
955 return strCalcValue;
956}
957#endif /* just for reference */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use