VirtualBox

Changeset 93190 in vbox


Ignore:
Timestamp:
Jan 11, 2022 11:22:13 PM (3 years ago)
Author:
vboxsync
Message:

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. bugref:9781

Location:
trunk/src/VBox/Main
Files:
3 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/include/MachineImpl.h

    r93115 r93190  
    4646# include "Performance.h"
    4747# include "PerformanceImpl.h"
    48 # include "ThreadTask.h"
    4948#endif
     49#include "ThreadTask.h"
    5050
    5151// generated header
  • trunk/src/VBox/Main/include/UnattendedScript.h

    r93115 r93190  
    2323
    2424#include "TextScript.h"
     25#include "iprt/expreval.h"
    2526
    2627using namespace xml;
     
    5253
    5354protected:
     55    typedef enum
     56    {
     57        kValueEscaping_None,
     58        kValueEscaping_Bourne,
     59        kValueEscaping_XML_Element,
     60        kValueEscaping_XML_Attribute_Double_Quotes
     61    } kEvalEscaping_T;
     62
    5463    /**
    5564     * Gets the replacement value for the given placeholder.
     
    6574
    6675    /**
    67      * Overridable worker for getReplacement.
     76     * Gets the replacement value for the given expression placeholder
     77     * (@@VBOX_INSERT[expr]@@ and friends).
    6878     *
    6979     * @returns COM status code.
    70      * @param   pachPlaceholder     The placholder string.  Not zero terminated.
    71      * @param   cchPlaceholder      The length of the placeholder.
    72      * @param   cchFullPlaceholder  The full placeholder length, including suffixes
    73      *                              indicating how it should be escaped (for error
    74      *                              messages).
    75      * @param   fOutputting         Indicates whether we actually need the correct
    76      *                              value or is just syntax checking excluded
    77      *                              template parts.  Intended for voiding triggering
    78      *                              sanity checks regarding which replacements
    79      *                              should be used and not (e.g. no Guest Additions
    80      *                              path when installing GAs aren't enabled).
    81      * @param   rValue              Where to return the value.
     80     * @param   hEvaluator      The evaluator to use for the expression.
     81     * @param   pachPlaceholder The placholder string.  Not zero terminated.
     82     * @param   cchPlaceholder  The length of the placeholder.
     83     * @param   fOutputting     Indicates whether we actually need the correct value
     84     *                          or is just syntax checking excluded template parts.
     85     * @param   ppszValue       Where to return the value.  Free by calling
     86     *                          RTStrFree.  Set to NULL for empty string.
     87     */
     88    HRESULT getReplacementForExpr(RTEXPREVAL hEvaluator, const char *pachPlaceholder, size_t cchPlaceholder,
     89                                  bool fOutputting, char **ppszValue) RT_NOEXCEPT;
     90
     91    /**
     92     * Resolves a conditional expression.
     93     *
     94     * @returns COM status code.
     95     * @param   hEvaluator      The evaluator to use for the expression.
     96     * @param   pachPlaceholder The placholder string.  Not zero terminated.
     97     * @param   cchPlaceholder  The length of the placeholder.
     98     * @param   pfOutputting    Where to return the result of the conditional. This
     99     *                          holds the current outputting state on input in case
     100     *                          someone want to sanity check anything.
     101     */
     102    HRESULT resolveConditionalExpr(RTEXPREVAL hEvaluator, const char *pachPlaceholder, size_t cchPlaceholder,
     103                                   bool *pfOutputting) RT_NOEXCEPT;
     104
     105    /** @impl_callback_method{FNRTEXPREVALQUERYVARIABLE}  */
     106    static DECLCALLBACK(int) queryVariableForExpr(const char *pchName, size_t cchName, void *pvUser,
     107                                                  char **ppszValue) RT_NOEXCEPT;
     108
     109    /**
     110     * Gets a variable.
     111     *
     112     * This is used both for getting replacements (@@VBOX_INSERT_XXX@@) and in
     113     * expressions (@@VBOX_INSERT[expr]@@, @@VBOX_COND[expr]@@).
     114     *
     115     * @returns VBox status code.
     116     * @retval  VERR_NOT_FOUND if variable does not exist.
     117     *
     118     * @param   pchName             The variable name.  Not zero terminated.
     119     * @param   cchName             The length of the name.
     120     * @param   rstrTmp             String object that can be used for keeping the
     121     *                              value returned via @a *ppszValue.
     122     * @param   ppszValue           If a value is desired, this is where to return
     123     *                              it.  This points to a string that should be
     124     *                              accessible for a little while after the function
     125     *                              returns.  Use @a rstrTmp for storage if
     126     *                              necessary.
     127     *
     128     *                              This will be NULL when called from the 'defined'
     129     *                              operator.  In which case no errors should be
     130     *                              set.
    82131     * @throws  std::bad_alloc
     132     * @see     FNRTEXPREVALQUERYVARIABLE
    83133     */
    84     virtual HRESULT getUnescapedReplacement(const char *pachPlaceholder, size_t cchPlaceholder,
    85                                             size_t cchFullPlaceholder, bool fOutputting, RTCString &rValue);
    86 
     134    virtual int queryVariable(const char *pchName, size_t cchName, Utf8Str &rstrTmp, const char **ppszValue);
    87135
    88136    /**
     
    97145     */
    98146    virtual HRESULT getConditional(const char *pachPlaceholder, size_t cchPlaceholder, bool *pfOutputting);
    99 
    100147};
    101148
  • trunk/src/VBox/Main/src-server/UnattendedScript.cpp

    r93121 r93190  
    2929#include "UnattendedImpl.h"
    3030
    31 #include <iprt/errcore.h>
     31#include <iprt/err.h>
    3232
    3333#include <iprt/ctype.h>
     
    4343
    4444/*********************************************************************************************************************************
     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/*********************************************************************************************************************************
    4560*   UnattendedScriptTemplate Implementation                                                                                      *
    4661*********************************************************************************************************************************/
     
    5267}
    5368
    54 
    5569HRESULT UnattendedScriptTemplate::saveToString(Utf8Str &rStrDst)
    5670{
    57     static const char s_szPrefix[]         = "@@VBOX_";
    58     static const char s_szPrefixInsert[]   = "@@VBOX_INSERT_";
    59     static const char s_szPrefixCond[]     = "@@VBOX_COND_";
    60     static const char s_szPrefixCondElse[] = "@@VBOX_COND_ELSE@@";
    61     static const char s_szPrefixCondEnd[]  = "@@VBOX_COND_END@@";
    62     static const char s_szPrefixSplitter[] = "@@VBOX_SPLITTER";
     71    RTEXPREVAL hEvaluator = NIL_RTEXPREVAL;
     72    int vrc = RTExprEvalCreate(&hEvaluator, 0, "unattended", this, UnattendedScriptTemplate::queryVariableForExpr);
     73    AssertRCReturn(vrc, mpSetError->setErrorVrc(vrc));
    6374
    6475    struct
     
    7788         * Find the next placeholder and add any text before it to the output.
    7889         */
    79         size_t offPlaceholder = mStrScriptFullContent.find(s_szPrefix, offTemplate);
     90        size_t offPlaceholder = mStrScriptFullContent.find(g_szPrefix, offTemplate);
    8091        size_t cchToCopy = offPlaceholder != RTCString::npos ? offPlaceholder - offTemplate : cchTemplate - offTemplate;
    8192        if (cchToCopy > 0)
     
    104115             * First we must find the end of the placeholder string.
    105116             */
    106             const char *pszPlaceholder = mStrScriptFullContent.c_str() + offPlaceholder;
    107             size_t      cchPlaceholder = sizeof(s_szPrefix) - 1;
    108             char        ch;
     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;
    109121            while (   offPlaceholder + cchPlaceholder < cchTemplate
    110122                   && (ch = pszPlaceholder[cchPlaceholder]) != '\0'
    111                    && (   ch == '_'
    112                        || ch == '['
    113                        || ch == ']'
    114                        || ch == '.'
    115                        || ch == '>'
    116                        || ch == '<'
    117                        || RT_C_IS_UPPER(ch)
    118                        || RT_C_IS_DIGIT(ch)) )
     123                   && (RT_C_IS_PRINT(ch) || RT_C_IS_SPACE(ch))
     124                   && ch != '@')
    119125                cchPlaceholder++;
    120126
     
    130136            if (   pszPlaceholder[cchPlaceholder - 1] != '@'
    131137                || pszPlaceholder[cchPlaceholder - 2] != '@'
    132                 || (   strncmp(pszPlaceholder, s_szPrefixInsert,   sizeof(s_szPrefixInsert)   - 1) != 0
    133                     && strncmp(pszPlaceholder, s_szPrefixCond,     sizeof(s_szPrefixCond)     - 1) != 0
    134                     && strncmp(pszPlaceholder, s_szPrefixSplitter, sizeof(s_szPrefixSplitter) - 1) != 0 ) )
    135             {
    136                 hrc = mpSetError->setError(E_FAIL, tr("Malformed template placeholder '%.*s'"),
     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'"),
    137143                                           cchPlaceholder, pszPlaceholder);
    138144                break;
     
    144150             * @@VBOX_INSERT_XXX@@:
    145151             */
    146             if ( strncmp(pszPlaceholder, s_szPrefixInsert, sizeof(s_szPrefixInsert) - 1) == 0 )
     152            if (strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixInsertXxx)) == 0)
    147153            {
    148154                /*
     
    170176            }
    171177            /*
     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            /*
    172210             * @@VBOX_COND_END@@: Pop one item of the conditional stack.
    173211             */
    174             else if ( strncmp(pszPlaceholder, s_szPrefixCondEnd, sizeof(s_szPrefixCondEnd) - 1U) == 0 )
     212            else if (strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixCondEnd)) == 0)
    175213            {
    176214                if (cConds > 0)
     
    183221                    hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
    184222                                                   tr("%s without @@VBOX_COND_XXX@@ at offset %zu (%#zx)"),
    185                                                    s_szPrefixCondEnd, offPlaceholder, offPlaceholder);
     223                                                   g_szPrefixCondEnd, offPlaceholder, offPlaceholder);
    186224                    break;
    187225                }
     
    190228             * @@VBOX_COND_ELSE@@: Flip the output setting of the current condition.
    191229             */
    192             else if ( strncmp(pszPlaceholder, s_szPrefixCondElse, sizeof(s_szPrefixCondElse) - 1U) == 0 )
     230            else if (strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixCondElse)) == 0)
    193231            {
    194232                if (cConds > 0)
     
    198236                    hrc = mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR,
    199237                                                   tr("%s without @@VBOX_COND_XXX@@ at offset %zu (%#zx)"),
    200                                                    s_szPrefixCondElse, offPlaceholder, offPlaceholder);
     238                                                   g_szPrefixCondElse, offPlaceholder, offPlaceholder);
    201239                    break;
    202240                }
     
    206244             *                    one from the condition.
    207245             */
    208             else if (strncmp(pszPlaceholder, s_szPrefixSplitter, sizeof(s_szPrefixSplitter) - 1) != 0)
    209             {
    210                 Assert(strncmp(pszPlaceholder, s_szPrefixCond, sizeof(s_szPrefixCond) - 1) == 0);
     246            else if (strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixCondXxx)) == 0)
     247            {
    211248                if (cConds + 1 < RT_ELEMENTS(aConds))
    212249                {
     
    229266            }
    230267            /*
     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            /*
    231293             * @@VBOX_SPLITTER_START/END[filename]@@: Ignored in this pass.
    232294             */
    233295            else
    234296            {
     297                Assert(strncmp(pszPlaceholder, RT_STR_TUPLE(g_szPrefixSplitter)) == 0);
    235298                if (fOutputting)
    236299                {
     
    265328    /* failed */
    266329    rStrDst.setNull();
     330    RTExprEvalRelease(hEvaluator);
    267331    return hrc;
    268332}
     
    274338     * Check for an escaping suffix.  Drop the '@@'.
    275339     */
    276     size_t const cchFullPlaceholder = cchPlaceholder;
    277     enum
    278     {
    279         kValueEscaping_None,
    280         kValueEscaping_Bourne,
    281         kValueEscaping_XML_Element,
    282         kValueEscaping_XML_Attribute_Double_Quotes
    283     } enmEscaping;
    284 
     340    size_t const    cchFullPlaceholder = cchPlaceholder;
     341    kEvalEscaping_T enmEscaping;
    285342#define PLACEHOLDER_ENDS_WITH(a_szSuffix) \
    286343        (   cchPlaceholder > sizeof(a_szSuffix) - 1U \
     
    307364        enmEscaping = kValueEscaping_None;
    308365    }
     366#undef PLACEHOLDER_ENDS_WITH
    309367
    310368    /*
     
    314372    try
    315373    {
    316         switch (enmEscaping)
    317         {
    318             case kValueEscaping_None:
    319                 hrc = getUnescapedReplacement(pachPlaceholder, cchPlaceholder, cchFullPlaceholder, fOutputting, rValue);
    320                 if (SUCCEEDED(hrc))
    321                     return hrc;
    322                 break;
    323 
    324             case kValueEscaping_Bourne:
    325             case kValueEscaping_XML_Element:
    326             case kValueEscaping_XML_Attribute_Double_Quotes:
    327             {
    328                 RTCString strUnescaped;
    329                 hrc = getUnescapedReplacement(pachPlaceholder, cchPlaceholder, cchFullPlaceholder, fOutputting, strUnescaped);
    330                 if (SUCCEEDED(hrc))
    331                 {
    332                     switch (enmEscaping)
     374        Utf8Str     strTmp;
     375        const char *pszReadOnlyValue = NULL;
     376        int vrc = queryVariable(pachPlaceholder + sizeof(g_szPrefixInsertXxx) - 1,
     377                                cchPlaceholder  - sizeof(g_szPrefixInsertXxx) + 1,
     378                                strTmp, fOutputting ? &pszReadOnlyValue : NULL);
     379        if (RT_SUCCESS(vrc))
     380        {
     381            if (fOutputting)
     382            {
     383                Assert(pszReadOnlyValue != NULL);
     384                switch (enmEscaping)
     385                {
     386                    case kValueEscaping_None:
     387                        rValue = pszReadOnlyValue;
     388                        return S_OK;
     389
     390                    case kValueEscaping_Bourne:
     391                    case kValueEscaping_XML_Element:
     392                    case kValueEscaping_XML_Attribute_Double_Quotes:
    333393                    {
    334                         case kValueEscaping_Bourne:
     394                        switch (enmEscaping)
    335395                        {
    336                             const char * const papszArgs[2] = { strUnescaped.c_str(), NULL };
    337                             char              *pszEscaped   = NULL;
    338                             int vrc = RTGetOptArgvToString(&pszEscaped, papszArgs, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
    339                             if (RT_SUCCESS(vrc))
     396                            case kValueEscaping_Bourne:
    340397                            {
    341                                 try
     398                                const char * const papszArgs[2] = { pszReadOnlyValue, NULL };
     399                                char              *pszEscaped   = NULL;
     400                                vrc = RTGetOptArgvToString(&pszEscaped, papszArgs, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
     401                                if (RT_SUCCESS(vrc))
    342402                                {
    343                                     rValue = pszEscaped;
     403                                    try
     404                                    {
     405                                        rValue = pszEscaped;
     406                                        RTStrFree(pszEscaped);
     407                                        return S_OK;
     408                                    }
     409                                    catch (std::bad_alloc &)
     410                                    {
     411                                        hrc = E_OUTOFMEMORY;
     412                                    }
    344413                                    RTStrFree(pszEscaped);
    345                                     return S_OK;
    346414                                }
    347                                 catch (std::bad_alloc &)
    348                                 {
    349                                     hrc = E_OUTOFMEMORY;
    350                                 }
    351                                 RTStrFree(pszEscaped);
     415                                else
     416                                    hrc = mpSetError->setErrorVrc(vrc);
     417                                break;
    352418                            }
    353                             break;
     419
     420                            case kValueEscaping_XML_Element:
     421                                rValue.printf("%RMes", pszReadOnlyValue);
     422                                return S_OK;
     423
     424                            case kValueEscaping_XML_Attribute_Double_Quotes:
     425                            {
     426                                RTCString strTmp2;
     427                                strTmp2.printf("%RMas", pszReadOnlyValue);
     428                                rValue = RTCString(strTmp2, 1, strTmp2.length() - 2);
     429                                return S_OK;
     430                            }
     431
     432                            default:
     433                                hrc = E_FAIL;
     434                                break;
    354435                        }
    355 
    356                         case kValueEscaping_XML_Element:
    357                             rValue.printf("%RMes", strUnescaped.c_str());
    358                             return S_OK;
    359 
    360                         case kValueEscaping_XML_Attribute_Double_Quotes:
    361                         {
    362                             RTCString strTmp;
    363                             strTmp.printf("%RMas", strUnescaped.c_str());
    364                             rValue = RTCString(strTmp, 1, strTmp.length() - 2);
    365                             return S_OK;
    366                         }
    367 
    368                         default:
    369                             hrc = E_FAIL;
    370                             break;
     436                        break;
    371437                    }
    372                 }
    373                 break;
    374             }
    375 
    376             default:
    377                 AssertFailedStmt(hrc = E_FAIL);
    378                 break;
    379         }
     438
     439                    default:
     440                        AssertFailedStmt(hrc = E_FAIL);
     441                        break;
     442                }
     443            }
     444            else
     445                hrc = S_OK;
     446        }
     447        else
     448            hrc = E_FAIL;
    380449    }
    381450    catch (std::bad_alloc &)
     
    387456}
    388457
    389 HRESULT UnattendedScriptTemplate::getUnescapedReplacement(const char *pachPlaceholder, size_t cchPlaceholder,
    390                                                           size_t cchFullPlaceholder, bool fOutputting, RTCString &rValue)
    391 {
    392     RT_NOREF(fOutputting);
    393 #define IS_PLACEHOLDER_MATCH(a_szMatch) \
    394         (   cchPlaceholder == sizeof("@@VBOX_INSERT_" a_szMatch) - 1U \
    395          && memcmp(pachPlaceholder, "@@VBOX_INSERT_" a_szMatch, sizeof("@@VBOX_INSERT_" a_szMatch) - 1U) == 0)
    396 
    397     if (IS_PLACEHOLDER_MATCH("USER_LOGIN"))
    398         rValue = mpUnattended->i_getUser();
    399     else if (IS_PLACEHOLDER_MATCH("USER_PASSWORD"))
    400         rValue = mpUnattended->i_getPassword();
    401     else if (IS_PLACEHOLDER_MATCH("ROOT_PASSWORD"))
    402         rValue = mpUnattended->i_getPassword();
    403     else if (IS_PLACEHOLDER_MATCH("USER_FULL_NAME"))
    404         rValue = mpUnattended->i_getFullUserName();
    405     else if (IS_PLACEHOLDER_MATCH("PRODUCT_KEY"))
    406         rValue = mpUnattended->i_getProductKey();
    407     else if (IS_PLACEHOLDER_MATCH("POST_INSTALL_COMMAND"))
    408         rValue = mpUnattended->i_getPostInstallCommand();
    409     else if (IS_PLACEHOLDER_MATCH("AUXILIARY_INSTALL_DIR"))
    410         rValue = mpUnattended->i_getAuxiliaryInstallDir();
    411     else if (IS_PLACEHOLDER_MATCH("IMAGE_INDEX"))
    412         rValue.printf("%u", mpUnattended->i_getImageIndex());
    413     else if (IS_PLACEHOLDER_MATCH("OS_ARCH"))
    414         rValue = mpUnattended->i_isGuestOs64Bit() ? "amd64" : "x86";
    415     else if (IS_PLACEHOLDER_MATCH("OS_ARCH2"))
    416         rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "x86";
    417     else if (IS_PLACEHOLDER_MATCH("OS_ARCH3"))
    418         rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i386";
    419     else if (IS_PLACEHOLDER_MATCH("OS_ARCH4"))
    420         rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i486";
    421     else if (IS_PLACEHOLDER_MATCH("OS_ARCH6"))
    422         rValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i686";
    423     else if (IS_PLACEHOLDER_MATCH("GUEST_OS_VERSION"))
    424         rValue = mpUnattended->i_getDetectedOSVersion();
    425     else if (IS_PLACEHOLDER_MATCH("GUEST_OS_MAJOR_VERSION"))
    426     {
    427         Utf8Str strOsVer(mpUnattended->i_getDetectedOSVersion());
    428         RTCList<RTCString> partList = strOsVer.split(".");
    429         if (partList.size() < 1)
    430         {
    431             rValue.setNull();
    432             return mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, tr("Unknown guest OS major version '%s'"),
    433                                             partList.at(0).c_str());
    434         }
    435         rValue = partList.at(0);
    436     }
    437     else if (IS_PLACEHOLDER_MATCH("TIME_ZONE_UX"))
    438         rValue = mpUnattended->i_getTimeZoneInfo()
    439                ? mpUnattended->i_getTimeZoneInfo()->pszUnixName : mpUnattended->i_getTimeZone();
    440     else if (IS_PLACEHOLDER_MATCH("TIME_ZONE_WIN_NAME"))
     458HRESULT UnattendedScriptTemplate::getReplacementForExpr(RTEXPREVAL hEvaluator, const char *pachPlaceholder, size_t cchPlaceholder,
     459                                                        bool fOutputting, char **ppszValue) RT_NOEXCEPT
     460{
     461    /*
     462     * Process the tail of the placeholder to figure out the escaping rules.
     463     *
     464     * @@VBOX_INSERT[expr]@@:
     465     * @@VBOX_INSERT[expr]SH@@:
     466     * @@VBOX_INSERT[expr]ELEMENT@@:
     467     * @@VBOX_INSERT[expr]ATTRIB_DQ@@:
     468     */
     469    size_t const    cchFullPlaceholder = cchPlaceholder;
     470    kEvalEscaping_T enmEscaping;
     471#define PLACEHOLDER_ENDS_WITH(a_szSuffix) \
     472        (   cchPlaceholder > sizeof(a_szSuffix) - 1U \
     473         && memcmp(&pachPlaceholder[cchPlaceholder - sizeof(a_szSuffix) + 1U], a_szSuffix, sizeof(a_szSuffix) - 1U) == 0)
     474    if (PLACEHOLDER_ENDS_WITH("]SH@@"))
     475    {
     476        cchPlaceholder -= sizeof("]SH@@") - 1;
     477        enmEscaping = kValueEscaping_Bourne;
     478    }
     479    else if (PLACEHOLDER_ENDS_WITH("]ELEMENT@@"))
     480    {
     481        cchPlaceholder -= sizeof("]ELEMENT@@") - 1;
     482        enmEscaping = kValueEscaping_XML_Element;
     483    }
     484    else if (PLACEHOLDER_ENDS_WITH("]ATTRIB_DQ@@"))
     485    {
     486        cchPlaceholder -= sizeof("]ATTRIB_DQ@@") - 1;
     487        enmEscaping = kValueEscaping_XML_Attribute_Double_Quotes;
     488    }
     489    else if (PLACEHOLDER_ENDS_WITH("]@@"))
     490    {
     491        cchPlaceholder -= sizeof("]@@") - 1;
     492        enmEscaping = kValueEscaping_None;
     493    }
     494    else
     495        return mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, tr("Malformed @@VBOX_INSERT[expr]@@: Missing ']' (%.*s)"),
     496                                        cchPlaceholder, pachPlaceholder);
     497#undef PLACEHOLDER_ENDS_WITH
     498
     499    /* The placeholder prefix length.  The expression is from cchPrefix to cchPlaceholder. */
     500    size_t const cchPrefix = sizeof(g_szPrefixInsertExpr) - 1;
     501    Assert(pachPlaceholder[cchPrefix - 1] == '[');
     502
     503    /*
     504     * Evaluate the expression.  We do this regardless of fOutput for now.
     505     */
     506    RTERRINFOSTATIC ErrInfo;
     507    char *pszValue = NULL;
     508    int vrc = RTExprEvalToString(hEvaluator, &pachPlaceholder[cchPrefix], cchPlaceholder - cchPrefix, &pszValue,
     509                                 RTErrInfoInitStatic(&ErrInfo));
     510    LogFlowFunc(("RTExprEvalToString(%.*s) -> %Rrc pszValue=%s\n",
     511                 cchPlaceholder - cchPrefix, &pachPlaceholder[cchPrefix], vrc, pszValue));
     512    if (RT_SUCCESS(vrc))
     513    {
     514        if (fOutputting)
     515        {
     516            switch (enmEscaping)
     517            {
     518                case kValueEscaping_None:
     519                    *ppszValue = pszValue;
     520                    pszValue = NULL;
     521                    break;
     522
     523                case kValueEscaping_Bourne:
     524                {
     525                    const char * const papszArgs[2] = { pszValue, NULL };
     526                    vrc = RTGetOptArgvToString(ppszValue, papszArgs, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
     527                    break;
     528                }
     529
     530                case kValueEscaping_XML_Element:
     531                    vrc = RTStrAPrintf(ppszValue, "%RMes", pszValue);
     532                    break;
     533
     534                case kValueEscaping_XML_Attribute_Double_Quotes:
     535                    vrc = RTStrAPrintf(ppszValue, "%RMas", pszValue);
     536                    if (RT_SUCCESS(vrc))
     537                    {
     538                        /* drop the quotes */
     539                        char *pszRet = *ppszValue;
     540                        size_t const cchRet = strlen(pszRet) - 2;
     541                        memmove(pszRet, &pszRet[1], cchRet);
     542                        pszRet[cchRet] = '\0';
     543                    }
     544                    break;
     545
     546                default:
     547                    AssertFailedStmt(vrc = VERR_IPE_NOT_REACHED_DEFAULT_CASE);
     548                    break;
     549            }
     550            RTStrFree(pszValue);
     551            if (RT_FAILURE(vrc))
     552                return mpSetError->setErrorVrc(vrc);
     553        }
     554        else
     555        {
     556            *ppszValue = NULL;
     557            RTStrFree(pszValue);
     558        }
     559    }
     560    else
     561        return mpSetError->setErrorBoth(E_FAIL, vrc, tr("Expression evaluation error for '%.*s': %#RTeic"),
     562                                        cchPlaceholder, pachPlaceholder, &ErrInfo.Core);
     563    return S_OK;
     564}
     565
     566HRESULT UnattendedScriptTemplate::resolveConditionalExpr(RTEXPREVAL hEvaluator, const char *pachPlaceholder,
     567                                                         size_t cchPlaceholder, bool *pfOutputting) RT_NOEXCEPT
     568{
     569    /*
     570     * Check the placeholder tail: @@VBOX_COND[expr]@@
     571     */
     572    static const char s_szTail[] = "]@@";
     573    if (memcmp(&pachPlaceholder[cchPlaceholder - sizeof(s_szTail) + 1], RT_STR_TUPLE(s_szTail)) != 0)
     574        return mpSetError->setErrorBoth(E_FAIL, VERR_PARSE_ERROR, tr("Malformed @@VBOX_COND[expr]@@: Missing ']' (%.*s)"),
     575                                        cchPlaceholder, pachPlaceholder);
     576    Assert(pachPlaceholder[sizeof(g_szPrefixCondExpr) - 2 ] == '[');
     577
     578    /*
     579     * Evaluate the expression.
     580     */
     581    RTERRINFOSTATIC    ErrInfo;
     582    const char * const pchExpr = &pachPlaceholder[sizeof(g_szPrefixCondExpr) - 1];
     583    size_t const       cchExpr = cchPlaceholder - sizeof(g_szPrefixCondExpr) + 1 - sizeof(s_szTail) + 1;
     584    int vrc = RTExprEvalToBool(hEvaluator, pchExpr, cchExpr, pfOutputting, RTErrInfoInitStatic(&ErrInfo));
     585    LogFlowFunc(("RTExprEvalToBool(%.*s) -> %Rrc *pfOutputting=%s\n", cchExpr, pchExpr, vrc, *pfOutputting));
     586    if (RT_SUCCESS(vrc))
     587        return S_OK;
     588    return mpSetError->setErrorBoth(E_FAIL, vrc, tr("Expression evaluation error for '%.*s': %#RTeic"),
     589                                    cchPlaceholder, pachPlaceholder, &ErrInfo.Core);
     590}
     591
     592/*static */ DECLCALLBACK(int)
     593UnattendedScriptTemplate::queryVariableForExpr(const char *pchName, size_t cchName, void *pvUser, char **ppszValue)
     594{
     595    UnattendedScriptTemplate *pThis = (UnattendedScriptTemplate *)pvUser;
     596    int vrc;
     597    try
     598    {
     599        const char *pszReadOnlyValue = NULL;
     600        Utf8Str     strTmp;
     601        vrc = pThis->queryVariable(pchName, cchName, strTmp, ppszValue ? &pszReadOnlyValue : NULL);
     602        if (ppszValue)
     603        {
     604            if (RT_SUCCESS(vrc))
     605                vrc = RTStrDupEx(ppszValue, pszReadOnlyValue);
     606            else
     607                *ppszValue = NULL;
     608        }
     609    }
     610    catch (std::bad_alloc &)
     611    {
     612        vrc = VERR_NO_MEMORY;
     613        *ppszValue = NULL;
     614    }
     615    return vrc;
     616}
     617
     618int UnattendedScriptTemplate::queryVariable(const char *pchName, size_t cchName, Utf8Str &rstrTmp, const char **ppszValue)
     619{
     620#define IS_MATCH(a_szMatch) \
     621        (cchName == sizeof(a_szMatch) - 1U && memcmp(pchName, a_szMatch, sizeof(a_szMatch) - 1U) == 0)
     622
     623    const char *pszValue;
     624
     625    /*
     626     * Variables
     627     */
     628    if (IS_MATCH("USER_LOGIN"))
     629        pszValue = mpUnattended->i_getUser().c_str();
     630    else if (IS_MATCH("USER_PASSWORD"))
     631        pszValue = mpUnattended->i_getPassword().c_str();
     632    else if (IS_MATCH("ROOT_PASSWORD"))
     633        pszValue = mpUnattended->i_getPassword().c_str();
     634    else if (IS_MATCH("USER_FULL_NAME"))
     635        pszValue = mpUnattended->i_getFullUserName().c_str();
     636    else if (IS_MATCH("PRODUCT_KEY"))
     637        pszValue = mpUnattended->i_getProductKey().c_str();
     638    else if (IS_MATCH("POST_INSTALL_COMMAND"))
     639        pszValue = mpUnattended->i_getPostInstallCommand().c_str();
     640    else if (IS_MATCH("AUXILIARY_INSTALL_DIR"))
     641        pszValue = mpUnattended->i_getAuxiliaryInstallDir().c_str();
     642    else if (IS_MATCH("IMAGE_INDEX"))
     643        pszValue = rstrTmp.printf("%u", mpUnattended->i_getImageIndex()).c_str();
     644    else if (IS_MATCH("OS_ARCH"))
     645        pszValue = mpUnattended->i_isGuestOs64Bit() ? "amd64" : "x86";
     646    else if (IS_MATCH("OS_ARCH2"))
     647        pszValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "x86";
     648    else if (IS_MATCH("OS_ARCH3"))
     649        pszValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i386";
     650    else if (IS_MATCH("OS_ARCH4"))
     651        pszValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i486";
     652    else if (IS_MATCH("OS_ARCH6"))
     653        pszValue = mpUnattended->i_isGuestOs64Bit() ? "x86_64" : "i686";
     654    else if (IS_MATCH("GUEST_OS_VERSION"))
     655        pszValue = mpUnattended->i_getDetectedOSVersion().c_str();
     656    else if (IS_MATCH("GUEST_OS_MAJOR_VERSION"))
     657    {
     658        Utf8Str const &rstrOsVer = mpUnattended->i_getDetectedOSVersion();
     659        size_t offDot = rstrOsVer.find('.');
     660        if (offDot > 0 && offDot != Utf8Str::npos)
     661            pszValue = rstrTmp.assign(rstrOsVer, 0, offDot).c_str(); /* caller catches std::bad_alloc */
     662        else if (!ppszValue)
     663            return VERR_NOT_FOUND;
     664        else
     665        {
     666            mpSetError->setErrorBoth(E_FAIL, VERR_NO_DATA, tr("Unknown guest OS major version '%s'"), rstrOsVer.c_str());
     667            return VERR_NO_DATA;
     668        }
     669    }
     670    else if (IS_MATCH("TIME_ZONE_UX"))
     671        pszValue = mpUnattended->i_getTimeZoneInfo()
     672                 ? mpUnattended->i_getTimeZoneInfo()->pszUnixName : mpUnattended->i_getTimeZone().c_str();
     673    else if (IS_MATCH("TIME_ZONE_WIN_NAME"))
    441674    {
    442675        PCRTTIMEZONEINFO pInfo = mpUnattended->i_getTimeZoneInfo();
    443676        if (pInfo)
    444             rValue = pInfo->pszWindowsName ? pInfo->pszWindowsName : "GMT";
     677            pszValue = pInfo->pszWindowsName ? pInfo->pszWindowsName : "GMT";
    445678        else
    446             rValue = mpUnattended->i_getTimeZone();
    447     }
    448     else if (IS_PLACEHOLDER_MATCH("TIME_ZONE_WIN_INDEX"))
     679            pszValue = mpUnattended->i_getTimeZone().c_str();
     680    }
     681    else if (IS_MATCH("TIME_ZONE_WIN_INDEX"))
    449682    {
    450683        PCRTTIMEZONEINFO pInfo = mpUnattended->i_getTimeZoneInfo();
    451684        if (pInfo)
    452             rValue.printf("%u", pInfo->idxWindows ? pInfo->idxWindows : 85 /*GMT*/);
     685            pszValue = rstrTmp.printf("%u", pInfo->idxWindows ? pInfo->idxWindows : 85 /*GMT*/).c_str();
    453686        else
    454             rValue = mpUnattended->i_getTimeZone();
    455     }
    456     else if (IS_PLACEHOLDER_MATCH("LOCALE"))
    457         rValue = mpUnattended->i_getLocale();
    458     else if (IS_PLACEHOLDER_MATCH("DASH_LOCALE"))
    459     {
    460         rValue = mpUnattended->i_getLocale();
    461         Assert(rValue[2] == '_');
    462         rValue.replace(2, 1, "-");
    463     }
    464     else if (IS_PLACEHOLDER_MATCH("LANGUAGE"))
    465         rValue = mpUnattended->i_getLanguage();
    466     else if (IS_PLACEHOLDER_MATCH("COUNTRY"))
    467         rValue = mpUnattended->i_getCountry();
    468     else if (IS_PLACEHOLDER_MATCH("HOSTNAME_FQDN"))
    469         rValue = mpUnattended->i_getHostname();
    470     else if (IS_PLACEHOLDER_MATCH("HOSTNAME_WITHOUT_DOMAIN"))
    471         rValue.assign(mpUnattended->i_getHostname(), 0, mpUnattended->i_getHostname().find("."));
    472     else if (IS_PLACEHOLDER_MATCH("HOSTNAME_WITHOUT_DOMAIN_MAX_15"))
    473         rValue.assign(mpUnattended->i_getHostname(), 0, RT_MIN(mpUnattended->i_getHostname().find("."), 15));
    474     else if (IS_PLACEHOLDER_MATCH("HOSTNAME_DOMAIN"))
    475         rValue.assign(mpUnattended->i_getHostname(), mpUnattended->i_getHostname().find(".") + 1);
    476     else if (IS_PLACEHOLDER_MATCH("PROXY"))
    477         rValue = mpUnattended->i_getProxy();
     687            pszValue = mpUnattended->i_getTimeZone().c_str();
     688    }
     689    else if (IS_MATCH("LOCALE"))
     690        pszValue = mpUnattended->i_getLocale().c_str();
     691    else if (IS_MATCH("DASH_LOCALE"))
     692    {
     693        Assert(mpUnattended->i_getLocale()[2] == '_');
     694        pszValue = rstrTmp.assign(mpUnattended->i_getLocale()).replace(2, 1, "-").c_str();
     695    }
     696    else if (IS_MATCH("LANGUAGE"))
     697        pszValue = mpUnattended->i_getLanguage().c_str();
     698    else if (IS_MATCH("COUNTRY"))
     699        pszValue = mpUnattended->i_getCountry().c_str();
     700    else if (IS_MATCH("HOSTNAME_FQDN"))
     701        pszValue = mpUnattended->i_getHostname().c_str();
     702    else if (IS_MATCH("HOSTNAME_WITHOUT_DOMAIN"))
     703        pszValue = rstrTmp.assign(mpUnattended->i_getHostname(), 0, mpUnattended->i_getHostname().find(".")).c_str();
     704    else if (IS_MATCH("HOSTNAME_WITHOUT_DOMAIN_MAX_15"))
     705        pszValue = rstrTmp.assign(mpUnattended->i_getHostname(), 0, RT_MIN(mpUnattended->i_getHostname().find("."), 15)).c_str();
     706    else if (IS_MATCH("HOSTNAME_DOMAIN"))
     707        pszValue = rstrTmp.assign(mpUnattended->i_getHostname(), mpUnattended->i_getHostname().find(".") + 1).c_str();
     708    else if (IS_MATCH("PROXY"))
     709        pszValue = mpUnattended->i_getProxy().c_str();
     710    /*
     711     * Indicator variables.
     712     */
     713    else if (IS_MATCH("IS_INSTALLING_ADDITIONS"))
     714        pszValue = mpUnattended->i_getInstallGuestAdditions() ? "1" : "0";
     715    else if (IS_MATCH("IS_USER_LOGIN_ADMINISTRATOR"))
     716        pszValue = mpUnattended->i_getUser().compare("Administrator", RTCString::CaseInsensitive) == 0 ? "1" : "0";
     717    else if (IS_MATCH("IS_INSTALLING_TEST_EXEC_SERVICE"))
     718        pszValue = mpUnattended->i_getInstallTestExecService() ? "1" : "0";
     719    else if (IS_MATCH("HAS_POST_INSTALL_COMMAND"))
     720        pszValue = mpUnattended->i_getPostInstallCommand().isNotEmpty() ? "1" : "0";
     721    else if (IS_MATCH("HAS_PRODUCT_KEY"))
     722        pszValue = mpUnattended->i_getProductKey().isNotEmpty() ? "1" : "0";
     723    else if (IS_MATCH("IS_MINIMAL_INSTALLATION"))
     724        pszValue = mpUnattended->i_isMinimalInstallation() ? "1" : "0";
     725    else if (IS_MATCH("IS_FIRMWARE_UEFI"))
     726        pszValue = mpUnattended->i_isFirmwareEFI() ? "1" : "0";
     727    else if (IS_MATCH("IS_RTC_USING_UTC"))
     728        pszValue = mpUnattended->i_isRtcUsingUtc() ? "1" : "0";
     729    else if (IS_MATCH("HAS_PROXY"))
     730        pszValue = mpUnattended->i_getProxy().isNotEmpty() ? "1" : "0";
     731    /*
     732     * Unknown variable.
     733     */
     734    else if (!ppszValue)
     735        return VERR_NOT_FOUND;
    478736    else
    479737    {
    480         rValue.setNull();
    481         return mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, tr("Unknown template placeholder '%.*s'"),
    482                                         cchFullPlaceholder, pachPlaceholder);
    483     }
    484     return S_OK;
    485 #undef IS_PLACEHOLDER_MATCH
     738        mpSetError->setErrorBoth(E_FAIL, VERR_NOT_FOUND, tr("Unknown variable '%.*s'"), cchName, pchName);
     739        return VERR_NO_DATA;
     740    }
     741    if (ppszValue)
     742        *ppszValue = pszValue;
     743    return VINF_SUCCESS;
    486744}
    487745
  • trunk/src/VBox/Main/testcase/Makefile.kmk

    r93115 r93190  
    3737        tstMediumLock \
    3838        tstBstr \
    39         tstGuid
     39        tstGuid \
     40        tstUnattendedScript
    4041  PROGRAMS.linux += \
    4142        $(if $(VBOX_WITH_USB),tstUSBProxyLinux,)
     
    268269
    269270
     271#
     272# tstUnattendedScript
     273#
     274tstUnattendedScript_TEMPLATE = VBOXMAINCLIENTTSTEXE
     275tstUnattendedScript_DEFS     = VBOX_WITH_UNATTENDED IN_VBOXSVC IN_TST_UNATTENDED_SCRIPT
     276tstUnattendedScript_INTERMEDIATES = \
     277        $(VBOX_MAIN_APIWRAPPER_GEN_HDRS) \
     278       $(VBOX_XML_SCHEMADEFS_H)
     279tstUnattendedScript_INCS     = \
     280        ../include \
     281     $(VBOX_MAIN_APIWRAPPER_INCS) \
     282     $(dir $(VBOX_XML_SCHEMADEFS_H))
     283tstUnattendedScript_SOURCES  = \
     284        tstUnattendedScript.cpp \
     285       ../src-server/UnattendedScript.cpp \
     286       ../src-all/TextScript.cpp \
     287       ../src-all/VirtualBoxBase.cpp \
     288       ../src-all/VirtualBoxErrorInfoImpl.cpp \
     289       ../src-all/AutoCaller.cpp \
     290       ../src-all/GlobalStatusConversion.cpp
     291tstUnattendedScript_LIBS     = \
     292        $(PATH_STAGE_LIB)/VBoxAPIWrap$(VBOX_SUFF_LIB)
     293
     294INSTALLS += tstUnattendedScriptFiles
     295tstUnattendedScriptFiles_TEMPLATE = VBOXMAINCLIENTTSTEXE
     296tstUnattendedScriptFiles_SOURCES = \
     297        tstUnattendedScript-1.template \
     298        tstUnattendedScript-1.expected
     299
     300
     301
    270302# generate rules.
    271303include $(FILE_KBUILD_SUB_FOOTER)
Note: See TracChangeset for help on using the changeset viewer.

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