VirtualBox

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

Last change on this file since 93839 was 93839, checked in by vboxsync, 3 years ago

Added the sign= in VBOX_COND_GUEST_VERSION

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

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