VirtualBox

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

Last change on this file since 101683 was 101683, checked in by vboxsync, 7 months ago

Main/Unattended|GuestOSType: Add an entry in the guest OS type to indicate the name of the additions install package instead of hardcoding it in the templates. Allows easy adaption if something changes and makes it possible to auto install guest additions for linux.arm64 guests, bugref:10542

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.4 KB
Line 
1/* $Id: UnattendedScript.cpp 101683 2023-10-31 12:38: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 else if (IS_MATCH("ADDITIONS_INSTALL_PACKAGE_NAME"))
722 pszValue = mpUnattended->i_getAdditionsInstallPackage().c_str();
723 /*
724 * Indicator variables.
725 */
726 else if (IS_MATCH("IS_INSTALLING_ADDITIONS"))
727 pszValue = mpUnattended->i_getInstallGuestAdditions() ? "1" : "0";
728 else if (IS_MATCH("IS_USER_LOGIN_ADMINISTRATOR"))
729 pszValue = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) == 0 ? "1" : "0";
730 else if (IS_MATCH("IS_INSTALLING_TEST_EXEC_SERVICE"))
731 pszValue = mpUnattended->i_getInstallTestExecService() ? "1" : "0";
732 else if (IS_MATCH("HAS_POST_INSTALL_COMMAND"))
733 pszValue = mpUnattended->i_getPostInstallCommand().isNotEmpty() ? "1" : "0";
734 else if (IS_MATCH("HAS_PRODUCT_KEY"))
735 pszValue = mpUnattended->i_getProductKey().isNotEmpty() ? "1" : "0";
736 else if (IS_MATCH("IS_MINIMAL_INSTALLATION"))
737 pszValue = mpUnattended->i_isMinimalInstallation() ? "1" : "0";
738 else if (IS_MATCH("IS_FIRMWARE_UEFI"))
739 pszValue = mpUnattended->i_isFirmwareEFI() ? "1" : "0";
740 else if (IS_MATCH("IS_RTC_USING_UTC"))
741 pszValue = mpUnattended->i_isRtcUsingUtc() ? "1" : "0";
742 else if (IS_MATCH("HAS_PROXY"))
743 pszValue = mpUnattended->i_getProxy().isNotEmpty() ? "1" : "0";
744 /*
745 * Unknown variable.
746 */
747 else if (!ppszValue)
748 return VERR_NOT_FOUND;
749 else
750 {
751 mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, tr("Unknown variable '%.*s'"), cchName, pchName);
752 return VERR_NO_DATA;
753 }
754 if (ppszValue)
755 *ppszValue = pszValue;
756 return VINF_SUCCESS;
757}
758
759HRESULT UnattendedScriptTemplate::getConditional(const char *pachPlaceholder, size_t cchPlaceholder, bool *pfOutputting)
760{
761#define IS_PLACEHOLDER_MATCH(a_szMatch) \
762 ( cchPlaceholder == sizeof("@@VBOX_COND_" a_szMatch "@@") - 1U \
763 && memcmp(pachPlaceholder, "@@VBOX_COND_" a_szMatch "@@", sizeof("@@VBOX_COND_" a_szMatch "@@") - 1U) == 0)
764
765 /* Install Guest Additions: */
766 if (IS_PLACEHOLDER_MATCH("IS_INSTALLING_ADDITIONS"))
767 *pfOutputting = mpUnattended->i_getInstallGuestAdditions();
768 else if (IS_PLACEHOLDER_MATCH("IS_NOT_INSTALLING_ADDITIONS"))
769 *pfOutputting = !mpUnattended->i_getInstallGuestAdditions();
770 /* User == Administrator: */
771 else if (IS_PLACEHOLDER_MATCH("IS_USER_LOGIN_ADMINISTRATOR"))
772 *pfOutputting = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) == 0;
773 else if (IS_PLACEHOLDER_MATCH("IS_USER_LOGIN_NOT_ADMINISTRATOR"))
774 *pfOutputting = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) != 0;
775 /* Install TXS: */
776 else if (IS_PLACEHOLDER_MATCH("IS_INSTALLING_TEST_EXEC_SERVICE"))
777 *pfOutputting = mpUnattended->i_getInstallTestExecService();
778 else if (IS_PLACEHOLDER_MATCH("IS_NOT_INSTALLING_TEST_EXEC_SERVICE"))
779 *pfOutputting = !mpUnattended->i_getInstallTestExecService();
780 /* Post install command: */
781 else if (IS_PLACEHOLDER_MATCH("HAS_POST_INSTALL_COMMAND"))
782 *pfOutputting = mpUnattended->i_getPostInstallCommand().isNotEmpty();
783 else if (IS_PLACEHOLDER_MATCH("HAS_NO_POST_INSTALL_COMMAND"))
784 *pfOutputting = mpUnattended->i_getPostInstallCommand().isEmpty();
785 /* Product key: */
786 else if (IS_PLACEHOLDER_MATCH("HAS_PRODUCT_KEY"))
787 *pfOutputting = mpUnattended->i_getProductKey().isNotEmpty();
788 else if (IS_PLACEHOLDER_MATCH("HAS_NO_PRODUCT_KEY"))
789 *pfOutputting = mpUnattended->i_getProductKey().isEmpty();
790 /* Minimal installation: */
791 else if (IS_PLACEHOLDER_MATCH("IS_MINIMAL_INSTALLATION"))
792 *pfOutputting = mpUnattended->i_isMinimalInstallation();
793 else if (IS_PLACEHOLDER_MATCH("IS_NOT_MINIMAL_INSTALLATION"))
794 *pfOutputting = !mpUnattended->i_isMinimalInstallation();
795 /* Is firmware UEFI: */
796 else if (IS_PLACEHOLDER_MATCH("IS_FIRMWARE_UEFI"))
797 *pfOutputting = mpUnattended->i_isFirmwareEFI();
798 else if (IS_PLACEHOLDER_MATCH("IS_NOT_FIRMWARE_UEFI"))
799 *pfOutputting = !mpUnattended->i_isFirmwareEFI();
800 /* Is RTC using UTC (i.e. set to UTC time on startup): */
801 else if (IS_PLACEHOLDER_MATCH("IS_RTC_USING_UTC"))
802 *pfOutputting = mpUnattended->i_isRtcUsingUtc();
803 else if (IS_PLACEHOLDER_MATCH("IS_NOT_RTC_USING_UTC"))
804 *pfOutputting = !mpUnattended->i_isRtcUsingUtc();
805 else if (IS_PLACEHOLDER_MATCH("HAS_PROXY"))
806 *pfOutputting = mpUnattended->i_getProxy().isNotEmpty();
807 else if (IS_PLACEHOLDER_MATCH("AVOID_UPDATES_OVER_NETWORK"))
808 *pfOutputting = mpUnattended->i_getAvoidUpdatesOverNetwork();
809 else
810 return mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, tr("Unknown conditional placeholder '%.*s'"),
811 cchPlaceholder, pachPlaceholder);
812 return S_OK;
813#undef IS_PLACEHOLDER_MATCH
814}
815
816#endif /* VBOX_WITH_UNATTENDED */
817#if 0 /* Keeping this a reference */
818
819
820/*********************************************************************************************************************************
821* UnattendedSUSEXMLScript Implementation *
822*********************************************************************************************************************************/
823
824HRESULT UnattendedSUSEXMLScript::parse()
825{
826 HRESULT hrc = UnattendedXMLScript::parse();
827 if (SUCCEEDED(hrc))
828 {
829 /*
830 * Check that we've got the right root element type.
831 */
832 const xml::ElementNode *pelmRoot = mDoc.getRootElement();
833 if ( pelmRoot
834 && strcmp(pelmRoot->getName(), "profile") == 0)
835 {
836 /*
837 * Work thought the sections.
838 */
839 try
840 {
841 LoopThruSections(pelmRoot);
842 hrc = S_OK;
843 }
844 catch (std::bad_alloc &)
845 {
846 hrc = E_OUTOFMEMORY;
847 }
848 }
849 else if (pelmRoot)
850 hrc = mpSetError->setError(E_FAIL, tr("XML document root element is '%s' instead of 'profile'"),
851 pelmRoot->getName());
852 else
853 hrc = mpSetError->setError(E_FAIL, tr("Missing XML root element"));
854 }
855 return hrc;
856}
857
858HRESULT UnattendedSUSEXMLScript::setFieldInElement(xml::ElementNode *pElement, const DataId enmDataId, const Utf8Str &rStrValue)
859{
860 /*
861 * Don't set empty values.
862 */
863 if (rStrValue.isEmpty())
864 {
865 Utf8Str strProbableValue;
866 try
867 {
868 strProbableValue = createProbableValue(enmDataId, pElement);
869 }
870 catch (std::bad_alloc &)
871 {
872 return E_OUTOFMEMORY;
873 }
874 return UnattendedXMLScript::setFieldInElement(pElement, enmDataId, strProbableValue);
875 }
876 return UnattendedXMLScript::setFieldInElement(pElement, enmDataId, rStrValue);
877}
878
879HRESULT UnattendedSUSEXMLScript::LoopThruSections(const xml::ElementNode *pelmRoot)
880{
881 xml::NodesLoop loopChildren(*pelmRoot);
882 const xml::ElementNode *pelmOuterLoop;
883 while ((pelmOuterLoop = loopChildren.forAllNodes()) != NULL)
884 {
885 const char *pcszElemName = pelmOuterLoop->getName();
886 if (!strcmp(pcszElemName, "users"))
887 {
888 xml::NodesLoop loopUsers(*pelmOuterLoop);
889 const xml::ElementNode *pelmUser;
890 while ((pelmUser = loopUsers.forAllNodes()) != NULL)
891 {
892 HRESULT hrc = HandleUserAccountsSection(pelmUser);
893 if (FAILED(hrc))
894 return hrc;
895 }
896 }
897 }
898 return S_OK;
899}
900
901HRESULT UnattendedSUSEXMLScript::HandleUserAccountsSection(const xml::ElementNode *pelmSection)
902{
903 xml::NodesLoop loopUser(*pelmSection);
904
905 const xml::ElementNode *pelmCur;
906 while ((pelmCur = loopUser.forAllNodes()) != NULL)
907 {
908 const char *pszValue = pelmCur->getValue();
909#ifdef LOG_ENABLED
910 if (!RTStrCmp(pelmCur->getName(), "uid"))
911 LogRelFunc(("UnattendedSUSEXMLScript::HandleUserAccountsSection profile/users/%s/%s = %s\n",
912 pelmSection->getName(), pelmCur->getName(), pszValue));
913#endif
914
915 if (!RTStrCmp(pszValue, "$homedir"))
916 mNodesForCorrectionMap.insert(make_pair(USERHOMEDIR_ID, pelmCur));
917
918 if (!RTStrCmp(pszValue, "$user"))
919 mNodesForCorrectionMap.insert(make_pair(USERNAME_ID, pelmCur));
920
921 if (!RTStrCmp(pszValue, "$password"))
922 mNodesForCorrectionMap.insert(make_pair(USERPASSWORD_ID, pelmCur));
923 }
924 return S_OK;
925}
926
927Utf8Str UnattendedSUSEXMLScript::createProbableValue(const DataId enmDataId, const xml::ElementNode *pCurElem)
928{
929 const xml::ElementNode *pElem = pCurElem;
930
931 switch (enmDataId)
932 {
933 case USERHOMEDIR_ID:
934// if ((pElem = pElem->findChildElement("home")))
935// {
936 return createProbableUserHomeDir(pElem);
937// }
938 break;
939 default:
940 break;
941 }
942
943 return Utf8Str::Empty;
944}
945
946Utf8Str UnattendedSUSEXMLScript::createProbableUserHomeDir(const xml::ElementNode *pCurElem)
947{
948 Utf8Str strCalcValue;
949 const xml::ElementNode *pElem = pCurElem->findNextSibilingElement("username");
950 if (pElem)
951 {
952 const char *pszValue = pElem->getValue();
953 strCalcValue = "/home/";
954 strCalcValue.append(pszValue);
955 }
956
957 return strCalcValue;
958}
959#endif /* just for reference */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use