VirtualBox

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

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

Main/UnattendedScript: Added @@VBOX_INSERT[expr]@@ and @@VBOX_COND[expr]@@, merging the variable & condition lookup with the previous replacement getter. Added simple testcase for checking the basics. [build fixes] bugref:9781

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 39.3 KB
Line 
1/* $Id: UnattendedScript.cpp 93191 2022-01-11 23:25:35Z 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 ( offPlaceholder + 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 return S_OK;
320 if (cConds == 1)
321 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, tr("Missing @@VBOX_COND_END@@"));
322 else
323 hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, tr("Missing %u @@VBOX_COND_END@@"), cConds);
324 break;
325 }
326 }
327
328 /* failed */
329 rStrDst.setNull();
330 RTExprEvalRelease(hEvaluator);
331 return hrc;
332}
333
334HRESULT UnattendedScriptTemplate::getReplacement(const char *pachPlaceholder, size_t cchPlaceholder,
335 bool fOutputting, RTCString &rValue)
336{
337 /*
338 * Check for an escaping suffix. Drop the '@@'.
339 */
340 kEvalEscaping_T enmEscaping;
341#define PLACEHOLDER_ENDS_WITH(a_szSuffix) \
342 ( cchPlaceholder > sizeof(a_szSuffix) - 1U \
343 && memcmp(&pachPlaceholder[cchPlaceholder - sizeof(a_szSuffix) + 1U], a_szSuffix, sizeof(a_szSuffix) - 1U) == 0)
344 if (PLACEHOLDER_ENDS_WITH("_SH@@"))
345 {
346 cchPlaceholder -= 3 + 2;
347 enmEscaping = kValueEscaping_Bourne;
348 }
349 else if (PLACEHOLDER_ENDS_WITH("_ELEMENT@@"))
350 {
351 cchPlaceholder -= 8 + 2;
352 enmEscaping = kValueEscaping_XML_Element;
353 }
354 else if (PLACEHOLDER_ENDS_WITH("_ATTRIB_DQ@@"))
355 {
356 cchPlaceholder -= 10 + 2;
357 enmEscaping = kValueEscaping_XML_Attribute_Double_Quotes;
358 }
359 else
360 {
361 Assert(PLACEHOLDER_ENDS_WITH("@@"));
362 cchPlaceholder -= 2;
363 enmEscaping = kValueEscaping_None;
364 }
365#undef PLACEHOLDER_ENDS_WITH
366
367 /*
368 * Resolve and escape the value.
369 */
370 HRESULT hrc;
371 try
372 {
373 Utf8Str strTmp;
374 const char *pszReadOnlyValue = NULL;
375 int vrc = queryVariable(pachPlaceholder + sizeof(g_szPrefixInsertXxx) - 1,
376 cchPlaceholder - sizeof(g_szPrefixInsertXxx) + 1,
377 strTmp, fOutputting ? &pszReadOnlyValue : NULL);
378 if (RT_SUCCESS(vrc))
379 {
380 if (fOutputting)
381 {
382 Assert(pszReadOnlyValue != NULL);
383 switch (enmEscaping)
384 {
385 case kValueEscaping_None:
386 rValue = pszReadOnlyValue;
387 return S_OK;
388
389 case kValueEscaping_Bourne:
390 case kValueEscaping_XML_Element:
391 case kValueEscaping_XML_Attribute_Double_Quotes:
392 {
393 switch (enmEscaping)
394 {
395 case kValueEscaping_Bourne:
396 {
397 const char * const papszArgs[2] = { pszReadOnlyValue, NULL };
398 char *pszEscaped = NULL;
399 vrc = RTGetOptArgvToString(&pszEscaped, papszArgs, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
400 if (RT_SUCCESS(vrc))
401 {
402 try
403 {
404 rValue = pszEscaped;
405 RTStrFree(pszEscaped);
406 return S_OK;
407 }
408 catch (std::bad_alloc &)
409 {
410 hrc = E_OUTOFMEMORY;
411 }
412 RTStrFree(pszEscaped);
413 }
414 else
415 hrc = mpSetError->setErrorVrc(vrc);
416 break;
417 }
418
419 case kValueEscaping_XML_Element:
420 rValue.printf("%RMes", pszReadOnlyValue);
421 return S_OK;
422
423 case kValueEscaping_XML_Attribute_Double_Quotes:
424 {
425 RTCString strTmp2;
426 strTmp2.printf("%RMas", pszReadOnlyValue);
427 rValue = RTCString(strTmp2, 1, strTmp2.length() - 2);
428 return S_OK;
429 }
430
431 default:
432 hrc = E_FAIL;
433 break;
434 }
435 break;
436 }
437
438 default:
439 AssertFailedStmt(hrc = E_FAIL);
440 break;
441 }
442 }
443 else
444 hrc = S_OK;
445 }
446 else
447 hrc = E_FAIL;
448 }
449 catch (std::bad_alloc &)
450 {
451 hrc = E_OUTOFMEMORY;
452 }
453 rValue.setNull();
454 return hrc;
455}
456
457HRESULT UnattendedScriptTemplate::getReplacementForExpr(RTEXPREVAL hEvaluator, const char *pachPlaceholder, size_t cchPlaceholder,
458 bool fOutputting, char **ppszValue) RT_NOEXCEPT
459{
460 /*
461 * Process the tail of the placeholder to figure out the escaping rules.
462 *
463 * @@VBOX_INSERT[expr]@@:
464 * @@VBOX_INSERT[expr]SH@@:
465 * @@VBOX_INSERT[expr]ELEMENT@@:
466 * @@VBOX_INSERT[expr]ATTRIB_DQ@@:
467 */
468 kEvalEscaping_T enmEscaping;
469#define PLACEHOLDER_ENDS_WITH(a_szSuffix) \
470 ( cchPlaceholder > sizeof(a_szSuffix) - 1U \
471 && memcmp(&pachPlaceholder[cchPlaceholder - sizeof(a_szSuffix) + 1U], a_szSuffix, sizeof(a_szSuffix) - 1U) == 0)
472 if (PLACEHOLDER_ENDS_WITH("]SH@@"))
473 {
474 cchPlaceholder -= sizeof("]SH@@") - 1;
475 enmEscaping = kValueEscaping_Bourne;
476 }
477 else if (PLACEHOLDER_ENDS_WITH("]ELEMENT@@"))
478 {
479 cchPlaceholder -= sizeof("]ELEMENT@@") - 1;
480 enmEscaping = kValueEscaping_XML_Element;
481 }
482 else if (PLACEHOLDER_ENDS_WITH("]ATTRIB_DQ@@"))
483 {
484 cchPlaceholder -= sizeof("]ATTRIB_DQ@@") - 1;
485 enmEscaping = kValueEscaping_XML_Attribute_Double_Quotes;
486 }
487 else if (PLACEHOLDER_ENDS_WITH("]@@"))
488 {
489 cchPlaceholder -= sizeof("]@@") - 1;
490 enmEscaping = kValueEscaping_None;
491 }
492 else
493 return mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, tr("Malformed @@VBOX_INSERT[expr]@@: Missing ']' (%.*s)"),
494 cchPlaceholder, pachPlaceholder);
495#undef PLACEHOLDER_ENDS_WITH
496
497 /* The placeholder prefix length. The expression is from cchPrefix to cchPlaceholder. */
498 size_t const cchPrefix = sizeof(g_szPrefixInsertExpr) - 1;
499 Assert(pachPlaceholder[cchPrefix - 1] == '[');
500
501 /*
502 * Evaluate the expression. We do this regardless of fOutput for now.
503 */
504 RTERRINFOSTATIC ErrInfo;
505 char *pszValue = NULL;
506 int vrc = RTExprEvalToString(hEvaluator, &pachPlaceholder[cchPrefix], cchPlaceholder - cchPrefix, &pszValue,
507 RTErrInfoInitStatic(&ErrInfo));
508 LogFlowFunc(("RTExprEvalToString(%.*s) -> %Rrc pszValue=%s\n",
509 cchPlaceholder - cchPrefix, &pachPlaceholder[cchPrefix], vrc, pszValue));
510 if (RT_SUCCESS(vrc))
511 {
512 if (fOutputting)
513 {
514 switch (enmEscaping)
515 {
516 case kValueEscaping_None:
517 *ppszValue = pszValue;
518 pszValue = NULL;
519 break;
520
521 case kValueEscaping_Bourne:
522 {
523 const char * const papszArgs[2] = { pszValue, NULL };
524 vrc = RTGetOptArgvToString(ppszValue, papszArgs, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
525 break;
526 }
527
528 case kValueEscaping_XML_Element:
529 vrc = RTStrAPrintf(ppszValue, "%RMes", pszValue);
530 break;
531
532 case kValueEscaping_XML_Attribute_Double_Quotes:
533 vrc = RTStrAPrintf(ppszValue, "%RMas", pszValue);
534 if (RT_SUCCESS(vrc))
535 {
536 /* drop the quotes */
537 char *pszRet = *ppszValue;
538 size_t const cchRet = strlen(pszRet) - 2;
539 memmove(pszRet, &pszRet[1], cchRet);
540 pszRet[cchRet] = '\0';
541 }
542 break;
543
544 default:
545 AssertFailedStmt(vrc = VERR_IPE_NOT_REACHED_DEFAULT_CASE);
546 break;
547 }
548 RTStrFree(pszValue);
549 if (RT_FAILURE(vrc))
550 return mpSetError->setErrorVrc(vrc);
551 }
552 else
553 {
554 *ppszValue = NULL;
555 RTStrFree(pszValue);
556 }
557 }
558 else
559 return mpSetError->setErrorBoth(E_FAIL, vrc, tr("Expression evaluation error for '%.*s': %#RTeic"),
560 cchPlaceholder, pachPlaceholder, &ErrInfo.Core);
561 return S_OK;
562}
563
564HRESULT UnattendedScriptTemplate::resolveConditionalExpr(RTEXPREVAL hEvaluator, const char *pachPlaceholder,
565 size_t cchPlaceholder, bool *pfOutputting) RT_NOEXCEPT
566{
567 /*
568 * Check the placeholder tail: @@VBOX_COND[expr]@@
569 */
570 static const char s_szTail[] = "]@@";
571 if (memcmp(&pachPlaceholder[cchPlaceholder - sizeof(s_szTail) + 1], RT_STR_TUPLE(s_szTail)) != 0)
572 return mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, tr("Malformed @@VBOX_COND[expr]@@: Missing ']' (%.*s)"),
573 cchPlaceholder, pachPlaceholder);
574 Assert(pachPlaceholder[sizeof(g_szPrefixCondExpr) - 2 ] == '[');
575
576 /*
577 * Evaluate the expression.
578 */
579 RTERRINFOSTATIC ErrInfo;
580 const char * const pchExpr = &pachPlaceholder[sizeof(g_szPrefixCondExpr) - 1];
581 size_t const cchExpr = cchPlaceholder - sizeof(g_szPrefixCondExpr) + 1 - sizeof(s_szTail) + 1;
582 int vrc = RTExprEvalToBool(hEvaluator, pchExpr, cchExpr, pfOutputting, RTErrInfoInitStatic(&ErrInfo));
583 LogFlowFunc(("RTExprEvalToBool(%.*s) -> %Rrc *pfOutputting=%s\n", cchExpr, pchExpr, vrc, *pfOutputting));
584 if (RT_SUCCESS(vrc))
585 return S_OK;
586 return mpSetError->setErrorBoth(E_FAIL, vrc, tr("Expression evaluation error for '%.*s': %#RTeic"),
587 cchPlaceholder, pachPlaceholder, &ErrInfo.Core);
588}
589
590/*static */ DECLCALLBACK(int)
591UnattendedScriptTemplate::queryVariableForExpr(const char *pchName, size_t cchName, void *pvUser, char **ppszValue) RT_NOEXCEPT
592{
593 UnattendedScriptTemplate *pThis = (UnattendedScriptTemplate *)pvUser;
594 int vrc;
595 try
596 {
597 const char *pszReadOnlyValue = NULL;
598 Utf8Str strTmp;
599 vrc = pThis->queryVariable(pchName, cchName, strTmp, ppszValue ? &pszReadOnlyValue : NULL);
600 if (ppszValue)
601 {
602 if (RT_SUCCESS(vrc))
603 vrc = RTStrDupEx(ppszValue, pszReadOnlyValue);
604 else
605 *ppszValue = NULL;
606 }
607 }
608 catch (std::bad_alloc &)
609 {
610 vrc = VERR_NO_MEMORY;
611 *ppszValue = NULL;
612 }
613 return vrc;
614}
615
616int UnattendedScriptTemplate::queryVariable(const char *pchName, size_t cchName, Utf8Str &rstrTmp, const char **ppszValue)
617{
618#define IS_MATCH(a_szMatch) \
619 (cchName == sizeof(a_szMatch) - 1U && memcmp(pchName, a_szMatch, sizeof(a_szMatch) - 1U) == 0)
620
621 const char *pszValue;
622
623 /*
624 * Variables
625 */
626 if (IS_MATCH("USER_LOGIN"))
627 pszValue = mpUnattended->i_getUser().c_str();
628 else if (IS_MATCH("USER_PASSWORD"))
629 pszValue = mpUnattended->i_getPassword().c_str();
630 else if (IS_MATCH("ROOT_PASSWORD"))
631 pszValue = mpUnattended->i_getPassword().c_str();
632 else if (IS_MATCH("USER_FULL_NAME"))
633 pszValue = mpUnattended->i_getFullUserName().c_str();
634 else if (IS_MATCH("PRODUCT_KEY"))
635 pszValue = mpUnattended->i_getProductKey().c_str();
636 else if (IS_MATCH("POST_INSTALL_COMMAND"))
637 pszValue = mpUnattended->i_getPostInstallCommand().c_str();
638 else if (IS_MATCH("AUXILIARY_INSTALL_DIR"))
639 pszValue = mpUnattended->i_getAuxiliaryInstallDir().c_str();
640 else if (IS_MATCH("IMAGE_INDEX"))
641 pszValue = rstrTmp.printf("%u", mpUnattended->i_getImageIndex()).c_str();
642 else if (IS_MATCH("OS_ARCH"))
643 pszValue = mpUnattended->i_isGuestOs64Bit() ? "amd64" : "x86";
644 else if (IS_MATCH("OS_ARCH2"))
645 pszValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "x86";
646 else if (IS_MATCH("OS_ARCH3"))
647 pszValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i386";
648 else if (IS_MATCH("OS_ARCH4"))
649 pszValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i486";
650 else if (IS_MATCH("OS_ARCH6"))
651 pszValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i686";
652 else if (IS_MATCH("GUEST_OS_VERSION"))
653 pszValue = mpUnattended->i_getDetectedOSVersion().c_str();
654 else if (IS_MATCH("GUEST_OS_MAJOR_VERSION"))
655 {
656 Utf8Str const &rstrOsVer = mpUnattended->i_getDetectedOSVersion();
657 size_t offDot = rstrOsVer.find('.');
658 if (offDot > 0 && offDot != Utf8Str::npos)
659 pszValue = rstrTmp.assign(rstrOsVer, 0, offDot).c_str(); /* caller catches std::bad_alloc */
660 else if (!ppszValue)
661 return VERR_NOT_FOUND;
662 else
663 {
664 mpSetError->setErrorBoth(E_FAIL, VERR_NO_DATA, tr("Unknown guest OS major version '%s'"), rstrOsVer.c_str());
665 return VERR_NO_DATA;
666 }
667 }
668 else if (IS_MATCH("TIME_ZONE_UX"))
669 pszValue = mpUnattended->i_getTimeZoneInfo()
670 ? mpUnattended->i_getTimeZoneInfo()->pszUnixName : mpUnattended->i_getTimeZone().c_str();
671 else if (IS_MATCH("TIME_ZONE_WIN_NAME"))
672 {
673 PCRTTIMEZONEINFO pInfo = mpUnattended->i_getTimeZoneInfo();
674 if (pInfo)
675 pszValue = pInfo->pszWindowsName ? pInfo->pszWindowsName : "GMT";
676 else
677 pszValue = mpUnattended->i_getTimeZone().c_str();
678 }
679 else if (IS_MATCH("TIME_ZONE_WIN_INDEX"))
680 {
681 PCRTTIMEZONEINFO pInfo = mpUnattended->i_getTimeZoneInfo();
682 if (pInfo)
683 pszValue = rstrTmp.printf("%u", pInfo->idxWindows ? pInfo->idxWindows : 85 /*GMT*/).c_str();
684 else
685 pszValue = mpUnattended->i_getTimeZone().c_str();
686 }
687 else if (IS_MATCH("LOCALE"))
688 pszValue = mpUnattended->i_getLocale().c_str();
689 else if (IS_MATCH("DASH_LOCALE"))
690 {
691 Assert(mpUnattended->i_getLocale()[2] == '_');
692 pszValue = rstrTmp.assign(mpUnattended->i_getLocale()).replace(2, 1, "-").c_str();
693 }
694 else if (IS_MATCH("LANGUAGE"))
695 pszValue = mpUnattended->i_getLanguage().c_str();
696 else if (IS_MATCH("COUNTRY"))
697 pszValue = mpUnattended->i_getCountry().c_str();
698 else if (IS_MATCH("HOSTNAME_FQDN"))
699 pszValue = mpUnattended->i_getHostname().c_str();
700 else if (IS_MATCH("HOSTNAME_WITHOUT_DOMAIN"))
701 pszValue = rstrTmp.assign(mpUnattended->i_getHostname(), 0, mpUnattended->i_getHostname().find(".")).c_str();
702 else if (IS_MATCH("HOSTNAME_WITHOUT_DOMAIN_MAX_15"))
703 pszValue = rstrTmp.assign(mpUnattended->i_getHostname(), 0, RT_MIN(mpUnattended->i_getHostname().find("."), 15)).c_str();
704 else if (IS_MATCH("HOSTNAME_DOMAIN"))
705 pszValue = rstrTmp.assign(mpUnattended->i_getHostname(), mpUnattended->i_getHostname().find(".") + 1).c_str();
706 else if (IS_MATCH("PROXY"))
707 pszValue = mpUnattended->i_getProxy().c_str();
708 /*
709 * Indicator variables.
710 */
711 else if (IS_MATCH("IS_INSTALLING_ADDITIONS"))
712 pszValue = mpUnattended->i_getInstallGuestAdditions() ? "1" : "0";
713 else if (IS_MATCH("IS_USER_LOGIN_ADMINISTRATOR"))
714 pszValue = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) == 0 ? "1" : "0";
715 else if (IS_MATCH("IS_INSTALLING_TEST_EXEC_SERVICE"))
716 pszValue = mpUnattended->i_getInstallTestExecService() ? "1" : "0";
717 else if (IS_MATCH("HAS_POST_INSTALL_COMMAND"))
718 pszValue = mpUnattended->i_getPostInstallCommand().isNotEmpty() ? "1" : "0";
719 else if (IS_MATCH("HAS_PRODUCT_KEY"))
720 pszValue = mpUnattended->i_getProductKey().isNotEmpty() ? "1" : "0";
721 else if (IS_MATCH("IS_MINIMAL_INSTALLATION"))
722 pszValue = mpUnattended->i_isMinimalInstallation() ? "1" : "0";
723 else if (IS_MATCH("IS_FIRMWARE_UEFI"))
724 pszValue = mpUnattended->i_isFirmwareEFI() ? "1" : "0";
725 else if (IS_MATCH("IS_RTC_USING_UTC"))
726 pszValue = mpUnattended->i_isRtcUsingUtc() ? "1" : "0";
727 else if (IS_MATCH("HAS_PROXY"))
728 pszValue = mpUnattended->i_getProxy().isNotEmpty() ? "1" : "0";
729 /*
730 * Unknown variable.
731 */
732 else if (!ppszValue)
733 return VERR_NOT_FOUND;
734 else
735 {
736 mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, tr("Unknown variable '%.*s'"), cchName, pchName);
737 return VERR_NO_DATA;
738 }
739 if (ppszValue)
740 *ppszValue = pszValue;
741 return VINF_SUCCESS;
742}
743
744HRESULT UnattendedScriptTemplate::getConditional(const char *pachPlaceholder, size_t cchPlaceholder, bool *pfOutputting)
745{
746#define IS_PLACEHOLDER_MATCH(a_szMatch) \
747 ( cchPlaceholder == sizeof("@@VBOX_COND_" a_szMatch "@@") - 1U \
748 && memcmp(pachPlaceholder, "@@VBOX_COND_" a_szMatch "@@", sizeof("@@VBOX_COND_" a_szMatch "@@") - 1U) == 0)
749#define IS_PLACEHOLDER_PARTIALLY_MATCH(a_szMatch) \
750 (memcmp(pachPlaceholder, "@@VBOX_COND_" a_szMatch, sizeof("@@VBOX_COND_" a_szMatch) - 1U) == 0)
751
752 /* Install Guest Additions: */
753 if (IS_PLACEHOLDER_MATCH("IS_INSTALLING_ADDITIONS"))
754 *pfOutputting = mpUnattended->i_getInstallGuestAdditions();
755 else if (IS_PLACEHOLDER_MATCH("IS_NOT_INSTALLING_ADDITIONS"))
756 *pfOutputting = !mpUnattended->i_getInstallGuestAdditions();
757 /* User == Administrator: */
758 else if (IS_PLACEHOLDER_MATCH("IS_USER_LOGIN_ADMINISTRATOR"))
759 *pfOutputting = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) == 0;
760 else if (IS_PLACEHOLDER_MATCH("IS_USER_LOGIN_NOT_ADMINISTRATOR"))
761 *pfOutputting = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) != 0;
762 /* Install TXS: */
763 else if (IS_PLACEHOLDER_MATCH("IS_INSTALLING_TEST_EXEC_SERVICE"))
764 *pfOutputting = mpUnattended->i_getInstallTestExecService();
765 else if (IS_PLACEHOLDER_MATCH("IS_NOT_INSTALLING_TEST_EXEC_SERVICE"))
766 *pfOutputting = !mpUnattended->i_getInstallTestExecService();
767 /* Post install command: */
768 else if (IS_PLACEHOLDER_MATCH("HAS_POST_INSTALL_COMMAND"))
769 *pfOutputting = mpUnattended->i_getPostInstallCommand().isNotEmpty();
770 else if (IS_PLACEHOLDER_MATCH("HAS_NO_POST_INSTALL_COMMAND"))
771 *pfOutputting = mpUnattended->i_getPostInstallCommand().isEmpty();
772 /* Product key: */
773 else if (IS_PLACEHOLDER_MATCH("HAS_PRODUCT_KEY"))
774 *pfOutputting = mpUnattended->i_getProductKey().isNotEmpty();
775 else if (IS_PLACEHOLDER_MATCH("HAS_NO_PRODUCT_KEY"))
776 *pfOutputting = mpUnattended->i_getProductKey().isEmpty();
777 /* Minimal installation: */
778 else if (IS_PLACEHOLDER_MATCH("IS_MINIMAL_INSTALLATION"))
779 *pfOutputting = mpUnattended->i_isMinimalInstallation();
780 else if (IS_PLACEHOLDER_MATCH("IS_NOT_MINIMAL_INSTALLATION"))
781 *pfOutputting = !mpUnattended->i_isMinimalInstallation();
782 /* Is firmware UEFI: */
783 else if (IS_PLACEHOLDER_MATCH("IS_FIRMWARE_UEFI"))
784 *pfOutputting = mpUnattended->i_isFirmwareEFI();
785 else if (IS_PLACEHOLDER_MATCH("IS_NOT_FIRMWARE_UEFI"))
786 *pfOutputting = !mpUnattended->i_isFirmwareEFI();
787 /* Is RTC using UTC (i.e. set to UTC time on startup): */
788 else if (IS_PLACEHOLDER_MATCH("IS_RTC_USING_UTC"))
789 *pfOutputting = mpUnattended->i_isRtcUsingUtc();
790 else if (IS_PLACEHOLDER_MATCH("IS_NOT_RTC_USING_UTC"))
791 *pfOutputting = !mpUnattended->i_isRtcUsingUtc();
792 else if (IS_PLACEHOLDER_MATCH("HAS_PROXY"))
793 *pfOutputting = mpUnattended->i_getProxy().isNotEmpty();
794 else if (IS_PLACEHOLDER_PARTIALLY_MATCH("GUEST_VERSION"))
795 {
796 //parse the placeholder and extract the OS version from there
797 RTCString strPlaceHolder(pachPlaceholder); /** @todo r=bird: What's the meaning of duplicating the rest of the script here
798 * when you could just add cchPlaceholder to the parameter list and limit it to
799 * what is actually needed. OTOH it's really not needed to make copies here,
800 * validating the "[" can be done in the partial match above and "]@@" by using
801 * cchPlaceholder, what you wnat to get at is inbetween and does not need
802 * two copies (only one for RTStrVersionCompare). */
803 size_t startPos = sizeof("@@VBOX_COND_GUEST_VERSION") - 1;//-1 is for '\n'
804 size_t endPos = strPlaceHolder.find("@@", startPos + 2);
805 //next part should look like [>8.0.0] for example where:
806 // - "[,]" is just the brackets to wrap up the condition;
807 // - ">" is "greater". Also possible comparison is "<";
808 // - 8.0.0 is required guest OS version.
809 //The end of placeholder is "@@" like for others.
810
811 /** @todo r=bird: What kind of syntax checking is this? Ignore any kind of
812 * mistyped stuff and let the user figure out what he did wrong
813 * without clues? Lazy. */
814 if ( strPlaceHolder[endPos] == '@'
815 && strPlaceHolder[endPos+1] == '@' )
816 {
817 if ( strPlaceHolder[startPos++] == '[' && strPlaceHolder[--endPos] == ']' )
818 {
819 char chComp = strPlaceHolder[startPos++];
820 RTCString strRequiredOSVersion = strPlaceHolder.substr(startPos, endPos - startPos);
821 RTCString strDetectedOSVersion = mpUnattended->i_getDetectedOSVersion();
822 int res = RTStrVersionCompare(strDetectedOSVersion.c_str(), strRequiredOSVersion.c_str());
823 if ( res >= 0 && chComp == '>' )
824 *pfOutputting = true;
825 else if ( res < 0 && chComp == '<' )
826 *pfOutputting = true;
827 else
828 *pfOutputting = false;
829 }
830 }
831 else
832 *pfOutputting = false;//initially is set to "false"
833 }
834 else
835 return mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, tr("Unknown conditional placeholder '%.*s'"),
836 cchPlaceholder, pachPlaceholder);
837 return S_OK;
838#undef IS_PLACEHOLDER_MATCH
839}
840
841#endif /* VBOX_WITH_UNATTENDED */
842#if 0 /* Keeping this a reference */
843
844
845/*********************************************************************************************************************************
846* UnattendedSUSEXMLScript Implementation *
847*********************************************************************************************************************************/
848
849HRESULT UnattendedSUSEXMLScript::parse()
850{
851 HRESULT hrc = UnattendedXMLScript::parse();
852 if (SUCCEEDED(hrc))
853 {
854 /*
855 * Check that we've got the right root element type.
856 */
857 const xml::ElementNode *pelmRoot = mDoc.getRootElement();
858 if ( pelmRoot
859 && strcmp(pelmRoot->getName(), "profile") == 0)
860 {
861 /*
862 * Work thought the sections.
863 */
864 try
865 {
866 LoopThruSections(pelmRoot);
867 hrc = S_OK;
868 }
869 catch (std::bad_alloc &)
870 {
871 hrc = E_OUTOFMEMORY;
872 }
873 }
874 else if (pelmRoot)
875 hrc = mpSetError->setError(E_FAIL, tr("XML document root element is '%s' instead of 'profile'"),
876 pelmRoot->getName());
877 else
878 hrc = mpSetError->setError(E_FAIL, tr("Missing XML root element"));
879 }
880 return hrc;
881}
882
883HRESULT UnattendedSUSEXMLScript::setFieldInElement(xml::ElementNode *pElement, const DataId enmDataId, const Utf8Str &rStrValue)
884{
885 /*
886 * Don't set empty values.
887 */
888 if (rStrValue.isEmpty())
889 {
890 Utf8Str strProbableValue;
891 try
892 {
893 strProbableValue = createProbableValue(enmDataId, pElement);
894 }
895 catch (std::bad_alloc &)
896 {
897 return E_OUTOFMEMORY;
898 }
899 return UnattendedXMLScript::setFieldInElement(pElement, enmDataId, strProbableValue);
900 }
901 return UnattendedXMLScript::setFieldInElement(pElement, enmDataId, rStrValue);
902}
903
904HRESULT UnattendedSUSEXMLScript::LoopThruSections(const xml::ElementNode *pelmRoot)
905{
906 xml::NodesLoop loopChildren(*pelmRoot);
907 const xml::ElementNode *pelmOuterLoop;
908 while ((pelmOuterLoop = loopChildren.forAllNodes()) != NULL)
909 {
910 const char *pcszElemName = pelmOuterLoop->getName();
911 if (!strcmp(pcszElemName, "users"))
912 {
913 xml::NodesLoop loopUsers(*pelmOuterLoop);
914 const xml::ElementNode *pelmUser;
915 while ((pelmUser = loopUsers.forAllNodes()) != NULL)
916 {
917 HRESULT hrc = HandleUserAccountsSection(pelmUser);
918 if (FAILED(hrc))
919 return hrc;
920 }
921 }
922 }
923 return S_OK;
924}
925
926HRESULT UnattendedSUSEXMLScript::HandleUserAccountsSection(const xml::ElementNode *pelmSection)
927{
928 xml::NodesLoop loopUser(*pelmSection);
929
930 const xml::ElementNode *pelmCur;
931 while ((pelmCur = loopUser.forAllNodes()) != NULL)
932 {
933 const char *pszValue = pelmCur->getValue();
934#ifdef LOG_ENABLED
935 if (!RTStrCmp(pelmCur->getName(), "uid"))
936 LogRelFunc(("UnattendedSUSEXMLScript::HandleUserAccountsSection profile/users/%s/%s = %s\n",
937 pelmSection->getName(), pelmCur->getName(), pszValue));
938#endif
939
940 if (!RTStrCmp(pszValue, "$homedir"))
941 mNodesForCorrectionMap.insert(make_pair(USERHOMEDIR_ID, pelmCur));
942
943 if (!RTStrCmp(pszValue, "$user"))
944 mNodesForCorrectionMap.insert(make_pair(USERNAME_ID, pelmCur));
945
946 if (!RTStrCmp(pszValue, "$password"))
947 mNodesForCorrectionMap.insert(make_pair(USERPASSWORD_ID, pelmCur));
948 }
949 return S_OK;
950}
951
952Utf8Str UnattendedSUSEXMLScript::createProbableValue(const DataId enmDataId, const xml::ElementNode *pCurElem)
953{
954 const xml::ElementNode *pElem = pCurElem;
955
956 switch (enmDataId)
957 {
958 case USERHOMEDIR_ID:
959// if ((pElem = pElem->findChildElement("home")))
960// {
961 return createProbableUserHomeDir(pElem);
962// }
963 break;
964 default:
965 break;
966 }
967
968 return Utf8Str::Empty;
969}
970
971Utf8Str UnattendedSUSEXMLScript::createProbableUserHomeDir(const xml::ElementNode *pCurElem)
972{
973 Utf8Str strCalcValue;
974 const xml::ElementNode *pElem = pCurElem->findNextSibilingElement("username");
975 if (pElem)
976 {
977 const char *pszValue = pElem->getValue();
978 strCalcValue = "/home/";
979 strCalcValue.append(pszValue);
980 }
981
982 return strCalcValue;
983}
984#endif /* just for reference */
985
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