VirtualBox

Changeset 42732 in vbox


Ignore:
Timestamp:
Aug 9, 2012 10:32:48 PM (13 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
79941
Message:

VBoxAutostart: Rewritten config parser for more flexibility in the future, changes config format

Location:
trunk/src/VBox/Frontends/VBoxAutostart
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Frontends/VBoxAutostart/VBoxAutostart.cpp

    r42527 r42732  
    331331        displayHeader();
    332332
    333     bool fAllowed = false;
    334     uint32_t uStartupDelay = 0;
    335     rc = autostartParseConfig(pszConfigFile, &fAllowed, &uStartupDelay);
     333    PCFGAST pCfgAst = NULL;
     334    char *pszUser = NULL;
     335    PCFGAST pCfgAstUser = NULL;
     336    PCFGAST pCfgAstPolicy = NULL;
     337    bool fAllow = false;
     338
     339    rc = autostartParseConfig(pszConfigFile, &pCfgAst);
    336340    if (RT_FAILURE(rc))
    337341        return RTEXITCODE_FAILURE;
    338342
    339     if (!fAllowed)
     343    rc = RTProcQueryUsernameA(RTProcSelf(), &pszUser);
     344    if (RT_FAILURE(rc))
     345        return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to query username of the process");
     346
     347    pCfgAstUser = autostartConfigAstGetByName(pCfgAst, pszUser);
     348    pCfgAstPolicy = autostartConfigAstGetByName(pCfgAst, "default_policy");
     349
     350    /* Check default policy. */
     351    if (pCfgAstPolicy)
     352    {
     353        if (   pCfgAstPolicy->enmType == CFGASTNODETYPE_KEYVALUE
     354            && (   !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "allow")
     355                || !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "deny")))
     356        {
     357            if (!RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "allow"))
     358                fAllow = true;
     359        }
     360        else
     361            return RTMsgErrorExit(RTEXITCODE_FAILURE, "'default_policy' must be either 'allow' or 'deny'");
     362    }
     363
     364    if (   pCfgAstUser
     365        && pCfgAstUser->enmType == CFGASTNODETYPE_COMPOUND)
     366    {
     367        pCfgAstPolicy = autostartConfigAstGetByName(pCfgAstUser, "allow");
     368        if (pCfgAstPolicy)
     369        {
     370            if (   pCfgAstPolicy->enmType == CFGASTNODETYPE_KEYVALUE
     371                && (   !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "true")
     372                    || !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "false")))
     373            {
     374                if (!RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "true"))
     375                    fAllow = true;
     376                else
     377                    fAllow = false;
     378            }
     379            else
     380                return RTMsgErrorExit(RTEXITCODE_FAILURE, "'allow' must be either 'true' or 'false'");
     381        }
     382    }
     383    else if (pCfgAstUser)
     384        return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid config, user is not a compound node");
     385
     386    if (!fAllow)
    340387        return RTMsgErrorExit(RTEXITCODE_FAILURE, "User is not allowed to autostart VMs");
     388
     389    RTStrFree(pszUser);
    341390
    342391    /* Don't start if the VirtualBox settings directory does not exist. */
     
    429478    RTEXITCODE rcExit;
    430479    if (fStart)
    431         rcExit = autostartStartMain(uStartupDelay);
     480        rcExit = autostartStartMain(pCfgAstUser);
    432481    else
    433482    {
    434483        Assert(fStop);
    435         rcExit = autostartStopMain(uStartupDelay);
    436     }
    437 
     484        rcExit = autostartStopMain(pCfgAstUser);
     485    }
     486
     487    autostartConfigAstDestroy(pCfgAst);
    438488    EventQueue::getMainEventQueue()->processEventQueue(0);
    439489
  • trunk/src/VBox/Frontends/VBoxAutostart/VBoxAutostart.h

    r42527 r42732  
    2828#include <VBox/com/VirtualBox.h>
    2929
     30/*******************************************************************************
     31*   Constants And Macros, Structures and Typedefs                              *
     32*******************************************************************************/
     33
     34/**
     35 * Config AST node types.
     36 */
     37typedef enum CFGASTNODETYPE
     38{
     39    /** Invalid. */
     40    CFGASTNODETYPE_INVALID = 0,
     41    /** Key/Value pair. */
     42    CFGASTNODETYPE_KEYVALUE,
     43    /** Compound type. */
     44    CFGASTNODETYPE_COMPOUND,
     45    /** List type. */
     46    CFGASTNODETYPE_LIST,
     47    /** 32bit hack. */
     48    CFGASTNODETYPE_32BIT_HACK = 0x7fffffff
     49} CFGASTNODETYPE;
     50/** Pointer to a config AST node type. */
     51typedef CFGASTNODETYPE *PCFGASTNODETYPE;
     52/** Pointer to a const config AST node type. */
     53typedef const CFGASTNODETYPE *PCCFGASTNODETYPE;
     54
     55/**
     56 * Config AST.
     57 */
     58typedef struct CFGAST
     59{
     60    /** AST node type. */
     61    CFGASTNODETYPE        enmType;
     62    /** Key or scope id. */
     63    char                 *pszKey;
     64    /** Type dependent data. */
     65    union
     66    {
     67        /** Key value pair. */
     68        struct
     69        {
     70            /** Number of characters in the value - excluding terminator. */
     71            size_t        cchValue;
     72            /** Value string - variable in size. */
     73            char          aszValue[1];
     74        } KeyValue;
     75        /** Compound type. */
     76        struct
     77        {
     78            /** Number of AST node entries in the array. */
     79            unsigned       cAstNodes;
     80            /** AST node array - variable in size. */
     81            struct CFGAST *apAstNodes[1];
     82        } Compound;
     83        /** List type. */
     84        struct
     85        {
     86            /** Number of entries in the list. */
     87            unsigned       cListEntries;
     88            /** Array of list entries - variable in size. */
     89            char          *apszEntries[1];
     90        } List;
     91    } u;
     92} CFGAST, *PCFGAST;
     93
    3094/** Flag whether we are in verbose logging mode. */
    3195extern bool                g_fVerbose;
     
    53117 * @returns VBox status code.
    54118 * @param   pszFilename    The config file to parse.
    55  * @param   pfAllowed      Where to store the flag whether the user of this process
    56  *                         is allowed to start VMs automatically during system startup.
    57  * @param   puStartupDelay Where to store the startup delay for the user.
     119 * @param   ppCfgAst       Where to store the pointer to the root AST node on success.
    58120 */
    59 DECLHIDDEN(int) autostartParseConfig(const char *pszFilename, bool *pfAllowed, uint32_t *puStartupDelay);
     121DECLHIDDEN(int) autostartParseConfig(const char *pszFilename, PCFGAST *ppCfgAst);
     122
     123/**
     124 * Destroys the config AST and frees all resources.
     125 *
     126 * @returns nothing.
     127 * @param   pCfgAst        The config AST.
     128 */
     129DECLHIDDEN(void) autostartConfigAstDestroy(PCFGAST pCfgAst);
     130
     131/**
     132 * Return the config AST node with the given name or NULL if it doesn't exist.
     133 *
     134 * @returns Matching config AST node for the given name or NULL if not found.
     135 * @param   pCfgAst    The config ASt to search.
     136 * @param   pszName    The name to search for.
     137 */
     138DECLHIDDEN(PCFGAST) autostartConfigAstGetByName(PCFGAST pCfgAst, const char *pszName);
    60139
    61140/**
     
    63142 *
    64143 * @returns exit status code.
    65  * @param   uStartupDelay    Base startup for the user.
     144 * @param   pCfgAst        Config AST for the startup part of the autostart daemon.
    66145 */
    67 DECLHIDDEN(RTEXITCODE) autostartStartMain(uint32_t uStartupDelay);
     146DECLHIDDEN(RTEXITCODE) autostartStartMain(PCFGAST pCfgAst);
    68147
    69148/**
     
    72151 *
    73152 * @returns exit status code.
    74  * @param   uStartupDelay    Base stop delay for the user.
     153 * @param   pCfgAst        Config AST for the shutdown part of the autostart daemon.
    75154 */
    76 DECLHIDDEN(RTEXITCODE) autostartStopMain(uint32_t uStopDelay);
     155DECLHIDDEN(RTEXITCODE) autostartStopMain(PCFGAST pCfgAst);
    77156
    78157#endif /* __VBoxAutostart_h__ */
  • trunk/src/VBox/Frontends/VBoxAutostart/VBoxAutostartCfg.cpp

    r42527 r42732  
    3232*   Constants And Macros, Structures and Typedefs                              *
    3333*******************************************************************************/
     34
     35/**
     36 * Token type.
     37 */
     38typedef enum CFGTOKENTYPE
     39{
     40    /** Invalid token type. */
     41    CFGTOKENTYPE_INVALID = 0,
     42    /** Identifier. */
     43    CFGTOKENTYPE_ID,
     44    /** Comma. */
     45    CFGTOKENTYPE_COMMA,
     46    /** Equal sign. */
     47    CFGTOKENTYPE_EQUAL,
     48    /** Open curly brackets. */
     49    CFGTOKENTYPE_CURLY_OPEN,
     50    /** Closing curly brackets. */
     51    CFGTOKENTYPE_CURLY_CLOSING,
     52    /** End of file. */
     53    CFGTOKENTYPE_EOF,
     54    /** 32bit hack. */
     55    CFGTOKENTYPE_32BIT_HACK = 0x7fffffff
     56} CFGTOKENTYPE;
     57/** Pointer to a token type. */
     58typedef CFGTOKENTYPE *PCFGTOKENTYPE;
     59/** Pointer to a const token type. */
     60typedef const CFGTOKENTYPE *PCCFGTOKENTYPE;
     61
     62/**
     63 * A token.
     64 */
     65typedef struct CFGTOKEN
     66{
     67    /** Type of the token. */
     68    CFGTOKENTYPE    enmType;
     69    /** Line number of the token. */
     70    unsigned        iLine;
     71    /** Starting character of the token in the stream. */
     72    unsigned        cchStart;
     73    /** Type dependen token data. */
     74    union
     75    {
     76        /** Data for the ID type. */
     77        struct
     78        {
     79            /** Size of the id in characters, excluding the \0 terminator. */
     80            size_t  cchToken;
     81            /** Token data, variable size (given by cchToken member). */
     82            char    achToken[1];
     83        } Id;
     84    } u;
     85} CFGTOKEN;
     86/** Pointer to a token. */
     87typedef CFGTOKEN *PCFGTOKEN;
     88/** Pointer to a const token. */
     89typedef const CFGTOKEN *PCCFGTOKEN;
    3490
    3591/**
     
    48104    /** Current line in the config file. */
    49105    unsigned   iLine;
     106    /** Current character of the line. */
     107    unsigned   cchCurr;
     108    /** Flag whether the end of the config stream is reached. */
     109    bool       fEof;
     110    /** Pointer to the next token in the stream (used to peek). */
     111    PCFGTOKEN  pTokenNext;
    50112} CFGTOKENIZER, *PCFGTOKENIZER;
    51113
     
    55117
    56118/**
     119 * Free a config token.
     120 *
     121 * @returns nothing.
     122 * @param   pCfgTokenizer    The config tokenizer.
     123 * @param   pToken           The token to free.
     124 */
     125static void autostartConfigTokenFree(PCFGTOKENIZER pCfgTokenizer, PCFGTOKEN pToken)
     126{
     127    NOREF(pCfgTokenizer);
     128    RTMemFree(pToken);
     129}
     130
     131/**
    57132 * Reads the next line from the config stream.
    58133 *
     
    63138{
    64139    int rc = VINF_SUCCESS;
     140
     141    if (pCfgTokenizer->fEof)
     142        return VERR_EOF;
    65143
    66144    do
     
    81159    } while (rc == VERR_BUFFER_OVERFLOW);
    82160
     161    if (   RT_SUCCESS(rc)
     162        || rc == VERR_EOF)
     163    {
     164        pCfgTokenizer->iLine++;
     165        pCfgTokenizer->cchCurr = 1;
     166        pCfgTokenizer->pszLineCurr = pCfgTokenizer->pszLine;
     167        if (rc == VERR_EOF)
     168            pCfgTokenizer->fEof = true;
     169    }
     170
     171    return rc;
     172}
     173
     174/**
     175 * Get the next token from the config stream and create a token structure.
     176 *
     177 * @returns VBox status code.
     178 * @param   pCfgTokenizer    The config tokenizer data.
     179 * @param   pCfgTokenUse     Allocated token structure to use or NULL to allocate
     180 *                           a new one. It will bee freed if an error is encountered.
     181 * @param   ppCfgToken       Where to store the pointer to the next token on success.
     182 */
     183static int autostartConfigTokenizerCreateToken(PCFGTOKENIZER pCfgTokenizer,
     184                                               PCFGTOKEN pCfgTokenUse, PCFGTOKEN *ppCfgToken)
     185{
     186    const char *pszToken = NULL;
     187    size_t cchToken = 1;
     188    size_t cchAdvance = 0;
     189    CFGTOKENTYPE enmType = CFGTOKENTYPE_INVALID;
     190    int rc = VINF_SUCCESS;
     191
     192    for (;;)
     193    {
     194        pszToken = pCfgTokenizer->pszLineCurr;
     195
     196        /* Skip all spaces. */
     197        while (RT_C_IS_BLANK(*pszToken))
     198        {
     199            pszToken++;
     200            cchAdvance++;
     201        }
     202
     203        /* Check if we have to read a new line. */
     204        if (   *pszToken == '\0'
     205            || *pszToken == '#')
     206        {
     207            rc = autostartConfigTokenizerReadNextLine(pCfgTokenizer);
     208            if (rc == VERR_EOF)
     209            {
     210                enmType = CFGTOKENTYPE_EOF;
     211                rc = VINF_SUCCESS;
     212                break;
     213            }
     214            else if (RT_FAILURE(rc))
     215                break;
     216            /* start from the beginning. */
     217            cchAdvance = 0;
     218        }
     219        else if (*pszToken == '=')
     220        {
     221            enmType = CFGTOKENTYPE_EQUAL;
     222            break;
     223        }
     224        else if (*pszToken == ',')
     225        {
     226            enmType = CFGTOKENTYPE_COMMA;
     227            break;
     228        }
     229        else if (*pszToken == '{')
     230        {
     231            enmType = CFGTOKENTYPE_CURLY_OPEN;
     232            break;
     233        }
     234        else if (*pszToken == '}')
     235        {
     236            enmType = CFGTOKENTYPE_CURLY_CLOSING;
     237            break;
     238        }
     239        else
     240        {
     241            const char *pszTmp = pszToken;
     242            cchToken = 0;
     243            enmType = CFGTOKENTYPE_ID;
     244
     245            /* Get the complete token. */
     246            while (   RT_C_IS_ALNUM(*pszTmp)
     247                   || *pszTmp == '_'
     248                   || *pszTmp == '.')
     249            {
     250                pszTmp++;
     251                cchToken++;
     252            }
     253            break;
     254        }
     255    }
     256
     257    Assert(RT_FAILURE(rc) || enmType != CFGTOKENTYPE_INVALID);
     258
    83259    if (RT_SUCCESS(rc))
    84260    {
    85         pCfgTokenizer->iLine++;
    86         pCfgTokenizer->pszLineCurr = pCfgTokenizer->pszLine;
     261        /* Free the given token if it is an ID or the current one is an ID token. */
     262        if (   pCfgTokenUse
     263            && (   pCfgTokenUse->enmType == CFGTOKENTYPE_ID
     264                || enmType == CFGTOKENTYPE_ID))
     265        {
     266            autostartConfigTokenFree(pCfgTokenizer, pCfgTokenUse);
     267            pCfgTokenUse = NULL;
     268        }
     269
     270        if (!pCfgTokenUse)
     271        {
     272            size_t cbToken = sizeof(CFGTOKEN);
     273            if (enmType == CFGTOKENTYPE_ID)
     274                cbToken += (cchToken + 1) * sizeof(char);
     275
     276            pCfgTokenUse = (PCFGTOKEN)RTMemAllocZ(cbToken);
     277            if (!pCfgTokenUse)
     278                rc = VERR_NO_MEMORY;
     279        }
     280
     281        if (RT_SUCCESS(rc))
     282        {
     283            /* Copy token data. */
     284            pCfgTokenUse->enmType  = enmType;
     285            pCfgTokenUse->cchStart = pCfgTokenizer->cchCurr;
     286            pCfgTokenUse->iLine    = pCfgTokenizer->iLine;
     287            if (enmType == CFGTOKENTYPE_ID)
     288            {
     289                pCfgTokenUse->u.Id.cchToken = cchToken;
     290                memcpy(pCfgTokenUse->u.Id.achToken, pszToken, cchToken);
     291            }
     292        }
     293        else if (pCfgTokenUse)
     294            autostartConfigTokenFree(pCfgTokenizer, pCfgTokenUse);
     295
     296        if (RT_SUCCESS(rc))
     297        {
     298            /* Set new position in config stream. */
     299            pCfgTokenizer->pszLineCurr += cchToken + cchAdvance;
     300            pCfgTokenizer->cchCurr     += cchToken + cchAdvance;
     301            *ppCfgToken                 = pCfgTokenUse;
     302        }
    87303    }
    88304
    89305    return rc;
     306}
     307
     308/**
     309 * Destroys the given config tokenizer.
     310 *
     311 * @returns nothing.
     312 * @param   pCfgTokenizer    The config tokenizer to destroy.
     313 */
     314static void autostartConfigTokenizerDestroy(PCFGTOKENIZER pCfgTokenizer)
     315{
     316    if (pCfgTokenizer->pszLine)
     317        RTMemFree(pCfgTokenizer->pszLine);
     318    if (pCfgTokenizer->hStrmConfig)
     319        RTStrmClose(pCfgTokenizer->hStrmConfig);
     320    if (pCfgTokenizer->pTokenNext)
     321        RTMemFree(pCfgTokenizer->pTokenNext);
     322    RTMemFree(pCfgTokenizer);
    90323}
    91324
     
    105338    if (pCfgTokenizer)
    106339    {
    107         pCfgTokenizer->iLine = 1;
     340        pCfgTokenizer->iLine = 0;
    108341        pCfgTokenizer->cbLine = 128;
    109342        pCfgTokenizer->pszLine = (char *)RTMemAllocZ(pCfgTokenizer->cbLine);
     
    112345            rc = RTStrmOpen(pszFilename, "r", &pCfgTokenizer->hStrmConfig);
    113346            if (RT_SUCCESS(rc))
     347            {
    114348                rc = autostartConfigTokenizerReadNextLine(pCfgTokenizer);
     349                if (RT_SUCCESS(rc))
     350                    rc = autostartConfigTokenizerCreateToken(pCfgTokenizer, NULL,
     351                                                             &pCfgTokenizer->pTokenNext);
     352            }
    115353        }
    116354        else
     
    124362    else if (   RT_FAILURE(rc)
    125363             && pCfgTokenizer)
    126     {
    127         if (pCfgTokenizer->pszLine)
    128             RTMemFree(pCfgTokenizer->pszLine);
    129         if (pCfgTokenizer->hStrmConfig)
    130             RTStrmClose(pCfgTokenizer->hStrmConfig);
    131         RTMemFree(pCfgTokenizer);
    132     }
     364        autostartConfigTokenizerDestroy(pCfgTokenizer);
    133365
    134366    return rc;
     
    136368
    137369/**
    138  * Destroys the given config tokenizer.
    139  *
    140  * @returns nothing.
    141  * @param   pCfgTokenizer    The config tokenizer to destroy.
    142  */
    143 static void autostartConfigTokenizerDestroy(PCFGTOKENIZER pCfgTokenizer)
    144 {
    145     if (pCfgTokenizer->pszLine)
    146         RTMemFree(pCfgTokenizer->pszLine);
    147     if (pCfgTokenizer->hStrmConfig)
    148         RTStrmClose(pCfgTokenizer->hStrmConfig);
    149     RTMemFree(pCfgTokenizer);
    150 }
    151 
    152 /**
    153  * Read the next token from the config file.
     370 * Return the next token from the config stream.
    154371 *
    155372 * @returns VBox status code.
    156  * @param   pCfgTokenizer    The config tokenizer data.
    157  * @param   ppszToken        Where to store the start to the next token on success.
    158  * @param   pcchToken        Where to store the number of characters of the next token
    159  *                           excluding the \0 terminator on success.
    160  */
    161 static int autostartConfigTokenizerReadNext(PCFGTOKENIZER pCfgTokenizer, const char **ppszToken,
    162                                             size_t *pcchToken)
    163 {
    164     if (!pCfgTokenizer->pszLineCurr)
    165         return VERR_EOF;
    166 
     373 * @param   pCfgTokenizer   The config tokenizer.
     374 * @param   ppCfgToken      Where to store the next token.
     375 */
     376static int autostartConfigTokenizerGetNextToken(PCFGTOKENIZER pCfgTokenizer,
     377                                                PCFGTOKEN *ppCfgToken)
     378{
     379    *ppCfgToken = pCfgTokenizer->pTokenNext;
     380    return autostartConfigTokenizerCreateToken(pCfgTokenizer, NULL, &pCfgTokenizer->pTokenNext);
     381}
     382
     383static const char *autostartConfigTokenToString(PCFGTOKEN pToken)
     384{
     385    switch (pToken->enmType)
     386    {
     387        case CFGTOKENTYPE_COMMA:
     388            return ",";
     389        case CFGTOKENTYPE_EQUAL:
     390            return "=";
     391        case CFGTOKENTYPE_CURLY_OPEN:
     392            return "{";
     393        case CFGTOKENTYPE_CURLY_CLOSING:
     394            return "}";
     395        case CFGTOKENTYPE_EOF:
     396            return "<EOF>";
     397        case CFGTOKENTYPE_ID:
     398            return pToken->u.Id.achToken;
     399        default:
     400            AssertFailed();
     401            return "<Invalid>";
     402    }
     403
     404    AssertFailed();
     405    return NULL;
     406}
     407
     408static size_t autostartConfigTokenGetLength(PCFGTOKEN pToken)
     409{
     410    switch (pToken->enmType)
     411    {
     412        case CFGTOKENTYPE_COMMA:
     413        case CFGTOKENTYPE_EQUAL:
     414        case CFGTOKENTYPE_CURLY_OPEN:
     415        case CFGTOKENTYPE_CURLY_CLOSING:
     416            return 1;
     417        case CFGTOKENTYPE_EOF:
     418            return 0;
     419        case CFGTOKENTYPE_ID:
     420            return strlen(pToken->u.Id.achToken);
     421        default:
     422            AssertFailed();
     423            return 0;
     424    }
     425
     426    AssertFailed();
     427    return 0;
     428}
     429
     430static void autostartConfigTokenizerMsgUnexpectedToken(PCFGTOKEN pToken, const char *pszExpected)
     431{
     432    RTMsgError("Unexpected token '%s' at %d:%d.%d, expected '%s'",
     433               autostartConfigTokenToString(pToken),
     434               pToken->iLine, pToken->cchStart,
     435               pToken->cchStart + autostartConfigTokenGetLength(pToken) - 1, pszExpected);
     436}
     437
     438/**
     439 * Verfies a token and consumes it.
     440 *
     441 * @returns VBox status code.
     442 * @param   pCfgTokenizer    The config tokenizer.
     443 * @param   pszTokenCheck    The token to check for.
     444 */
     445static int autostartConfigTokenizerCheckAndConsume(PCFGTOKENIZER pCfgTokenizer, CFGTOKENTYPE enmType)
     446{
    167447    int rc = VINF_SUCCESS;
    168 
    169     for (;;)
    170     {
    171         char *pszTok = pCfgTokenizer->pszLineCurr;
    172 
    173         /* Skip all spaces. */
    174         while (RT_C_IS_BLANK(*pszTok))
    175             pszTok++;
    176 
    177         /* Check if we have to read a new line. */
    178         if (   *pszTok == '\0'
    179             || *pszTok == '#')
    180         {
    181             rc = autostartConfigTokenizerReadNextLine(pCfgTokenizer);
    182             if (RT_FAILURE(rc))
    183                 break;
    184             /* start from the beginning. */
    185         }
    186         else if (   *pszTok == '='
    187                  || *pszTok == ',')
    188         {
    189             *ppszToken = pszTok;
    190             *pcchToken = 1;
    191             pCfgTokenizer->pszLineCurr = pszTok + 1;
    192             break;
    193         }
    194         else
    195         {
    196             /* Get the complete token. */
    197             size_t cchToken = 1;
    198             char *pszTmp = pszTok + 1;
    199 
    200             while (   RT_C_IS_ALNUM(*pszTmp)
    201                    || *pszTmp == '_')
    202             {
    203                 pszTmp++;
    204                 cchToken++;
    205             }
    206 
    207             *ppszToken = pszTok;
    208             *pcchToken = cchToken;
    209             pCfgTokenizer->pszLineCurr = pszTmp;
    210             break;
    211         }
    212     }
    213 
     448    PCFGTOKEN pCfgToken = NULL;
     449
     450    rc = autostartConfigTokenizerGetNextToken(pCfgTokenizer, &pCfgToken);
     451    if (RT_SUCCESS(rc))
     452    {
     453        if (pCfgToken->enmType != enmType)
     454        {
     455            autostartConfigTokenizerMsgUnexpectedToken(pCfgToken, "@todo");
     456            rc = VERR_INVALID_PARAMETER;
     457        }
     458
     459        autostartConfigTokenFree(pCfgTokenizer, pCfgToken);
     460    }
    214461    return rc;
    215462}
    216463
    217 static int autostartConfigTokenizerCheckAndConsume(PCFGTOKENIZER pCfgTokenizer, const char *pszTokCheck)
    218 {
    219     int rc = VINF_SUCCESS;
    220     const char *pszToken = NULL;
    221     size_t cchToken = 0;
    222 
    223     rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszToken, &cchToken);
    224     if (RT_SUCCESS(rc))
    225     {
    226         if (RTStrNCmp(pszToken, pszTokCheck, cchToken))
    227         {
    228             RTMsgError("Unexpected token at line %d, expected '%s'",
    229                        pCfgTokenizer->iLine, pszTokCheck);
    230             rc = VERR_INVALID_PARAMETER;
    231         }
    232     }
    233     return rc;
    234 }
    235 
    236 /**
    237  * Returns the start of the next token without consuming it.
     464/**
     465 * Consumes the next token in the stream.
    238466 *
    239467 * @returns VBox status code.
    240468 * @param   pCfgTokenizer    Tokenizer instance data.
    241  * @param   ppszTok          Where to store the start of the next token on success.
    242  */
    243 static int autostartConfigTokenizerPeek(PCFGTOKENIZER pCfgTokenizer, const char **ppszTok)
     469 */
     470static int autostartConfigTokenizerConsume(PCFGTOKENIZER pCfgTokenizer)
    244471{
    245472    int rc = VINF_SUCCESS;
    246 
    247     for (;;)
    248     {
    249         char *pszTok = pCfgTokenizer->pszLineCurr;
    250 
    251         /* Skip all spaces. */
    252         while (RT_C_IS_BLANK(*pszTok))
    253             pszTok++;
    254 
    255         /* Check if we have to read a new line. */
    256         if (   *pszTok == '\0'
    257             || *pszTok == '#')
    258         {
    259             rc = autostartConfigTokenizerReadNextLine(pCfgTokenizer);
    260             if (RT_FAILURE(rc))
    261                 break;
    262             /* start from the beginning. */
    263         }
    264         else
    265         {
    266             *ppszTok = pszTok;
     473    PCFGTOKEN pCfgToken = NULL;
     474
     475    rc = autostartConfigTokenizerGetNextToken(pCfgTokenizer, &pCfgToken);
     476    if (RT_SUCCESS(rc))
     477        autostartConfigTokenFree(pCfgTokenizer, pCfgToken);
     478
     479    return rc;
     480}
     481
     482/**
     483 * Returns the start of the next token without consuming it.
     484 *
     485 * @returns The next token without consuming it.
     486 * @param   pCfgTokenizer    Tokenizer instance data.
     487 */
     488DECLINLINE(PCFGTOKEN) autostartConfigTokenizerPeek(PCFGTOKENIZER pCfgTokenizer)
     489{
     490    return pCfgTokenizer->pTokenNext;
     491}
     492
     493/**
     494 * Check whether the next token is equal to the given one.
     495 *
     496 * @returns true if the next token in the stream is equal to the given one
     497 *          false otherwise.
     498 * @param   pszToken    The token to check for.
     499 */
     500DECLINLINE(bool) autostartConfigTokenizerPeekIsEqual(PCFGTOKENIZER pCfgTokenizer, CFGTOKENTYPE enmType)
     501{
     502    PCFGTOKEN pToken = autostartConfigTokenizerPeek(pCfgTokenizer);
     503    return pToken->enmType == enmType;
     504}
     505
     506/**
     507 * Parse a key value node and returns the AST.
     508 *
     509 * @returns VBox status code.
     510 * @param   pCfgTokenizer    The tokenizer for the config stream.
     511 * @param   pszKey           The key for the pair.
     512 * @param   ppCfgAst         Where to store the resulting AST on success.
     513 */
     514static int autostartConfigParseValue(PCFGTOKENIZER pCfgTokenizer, const char *pszKey,
     515                                     PCFGAST *ppCfgAst)
     516{
     517    int rc = VINF_SUCCESS;
     518    PCFGTOKEN pToken = NULL;
     519
     520    rc = autostartConfigTokenizerGetNextToken(pCfgTokenizer, &pToken);
     521    if (   RT_SUCCESS(rc)
     522        && pToken->enmType == CFGTOKENTYPE_ID)
     523    {
     524        PCFGAST pCfgAst = NULL;
     525
     526        pCfgAst = (PCFGAST)RTMemAllocZ(RT_OFFSETOF(CFGAST, u.KeyValue.aszValue[pToken->u.Id.cchToken + 1]));
     527        if (!pCfgAst)
     528            return VERR_NO_MEMORY;
     529
     530        pCfgAst->enmType = CFGASTNODETYPE_KEYVALUE;
     531        pCfgAst->pszKey  = RTStrDup(pszKey);
     532        if (!pCfgAst->pszKey)
     533        {
     534            RTMemFree(pCfgAst);
     535            return VERR_NO_MEMORY;
     536        }
     537
     538        memcpy(pCfgAst->u.KeyValue.aszValue, pToken->u.Id.achToken, pToken->u.Id.cchToken);
     539        pCfgAst->u.KeyValue.cchValue = pToken->u.Id.cchToken;
     540        *ppCfgAst = pCfgAst;
     541    }
     542    else
     543    {
     544        autostartConfigTokenizerMsgUnexpectedToken(pToken, "non reserved token");
     545        rc = VERR_INVALID_PARAMETER;
     546    }
     547
     548    return rc;
     549}
     550
     551/**
     552 * Parses a compound node constructing the AST and returning it on success.
     553 *
     554 * @returns VBox status code.
     555 * @param   pCfgTokenizer    The tokenizer for the config stream.
     556 * @param   pszScopeId       The scope ID of the compound node.
     557 * @param   ppCfgAst         Where to store the resulting AST on success.
     558 */
     559static int autostartConfigParseCompoundNode(PCFGTOKENIZER pCfgTokenizer, const char *pszScopeId,
     560                                            PCFGAST *ppCfgAst)
     561{
     562    int rc = VINF_SUCCESS;
     563    unsigned cAstNodesMax = 10;
     564    unsigned idxAstNodeCur = 0;
     565    PCFGAST pCfgAst = NULL;
     566
     567    pCfgAst = (PCFGAST)RTMemAllocZ(RT_OFFSETOF(CFGAST, u.Compound.apAstNodes[cAstNodesMax]));
     568    if (!pCfgAst)
     569        return VERR_NO_MEMORY;
     570
     571    pCfgAst->enmType = CFGASTNODETYPE_COMPOUND;
     572    pCfgAst->pszKey  = RTStrDup(pszScopeId);
     573    if (!pCfgAst->pszKey)
     574    {
     575        RTMemFree(pCfgAst);
     576        return VERR_NO_MEMORY;
     577    }
     578
     579    do
     580    {
     581        PCFGTOKEN pToken = NULL;
     582        PCFGAST pAstNode = NULL;
     583
     584        if (   autostartConfigTokenizerPeekIsEqual(pCfgTokenizer, CFGTOKENTYPE_CURLY_CLOSING)
     585            || autostartConfigTokenizerPeekIsEqual(pCfgTokenizer, CFGTOKENTYPE_EOF))
    267586            break;
    268         }
    269     }
     587
     588        rc = autostartConfigTokenizerGetNextToken(pCfgTokenizer, &pToken);
     589        if (   RT_SUCCESS(rc)
     590            && pToken->enmType == CFGTOKENTYPE_ID)
     591        {
     592            /* Next must be a = token in all cases at this place. */
     593            rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, CFGTOKENTYPE_EQUAL);
     594            if (RT_SUCCESS(rc))
     595            {
     596                /* Check whether this is a compound node. */
     597                if (autostartConfigTokenizerPeekIsEqual(pCfgTokenizer, CFGTOKENTYPE_CURLY_OPEN))
     598                {
     599                    rc = autostartConfigTokenizerConsume(pCfgTokenizer);
     600                    if (RT_SUCCESS(rc))
     601                        rc = autostartConfigParseCompoundNode(pCfgTokenizer, pToken->u.Id.achToken,
     602                                                              &pAstNode);
     603
     604                    if (RT_SUCCESS(rc))
     605                        rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, CFGTOKENTYPE_CURLY_CLOSING);
     606                }
     607                else
     608                    rc = autostartConfigParseValue(pCfgTokenizer, pToken->u.Id.achToken,
     609                                                   &pAstNode);
     610            }
     611        }
     612        else if (RT_SUCCESS(rc))
     613        {
     614            autostartConfigTokenizerMsgUnexpectedToken(pToken, "non reserved token");
     615            rc = VERR_INVALID_PARAMETER;
     616        }
     617
     618        /* Add to the current compound node. */
     619        if (RT_SUCCESS(rc))
     620        {
     621            Assert(idxAstNodeCur < cAstNodesMax);
     622            pCfgAst->u.Compound.apAstNodes[idxAstNodeCur] = pAstNode;
     623            idxAstNodeCur++;
     624            /** @todo: realloc if array is getting to small. */
     625        }
     626
     627        autostartConfigTokenFree(pCfgTokenizer, pToken);
     628
     629    } while (RT_SUCCESS(rc));
     630
     631    if (RT_SUCCESS(rc))
     632    {
     633        pCfgAst->u.Compound.cAstNodes = idxAstNodeCur;
     634        *ppCfgAst = pCfgAst;
     635    }
     636    else
     637        autostartConfigAstDestroy(pCfgAst);
    270638
    271639    return rc;
    272640}
    273641
    274 /**
    275  * Check whether the given token is a reserved token.
    276  *
    277  * @returns true if the token is reserved or false otherwise.
    278  * @param   pszToken    The token to check.
    279  * @param   cchToken    Size of the token in characters.
    280  */
    281 static bool autostartConfigTokenizerIsReservedToken(const char *pszToken, size_t cchToken)
    282 {
    283     if (   cchToken == 1
    284         && (   *pszToken == ','
    285             || *pszToken == '='))
    286         return true;
    287     else if (   cchToken > 1
    288              && (   !RTStrNCmp(pszToken, "default_policy", cchToken)
    289                  || !RTStrNCmp(pszToken, "exception_list", cchToken)))
    290         return true;
    291 
    292     return false;
    293 }
    294 
    295 DECLHIDDEN(int) autostartParseConfig(const char *pszFilename, bool *pfAllowed, uint32_t *puStartupDelay)
    296 {
     642DECLHIDDEN(int) autostartParseConfig(const char *pszFilename, PCFGAST *ppCfgAst)
     643{
     644    PCFGTOKENIZER pCfgTokenizer = NULL;
    297645    int rc = VINF_SUCCESS;
    298     char *pszUserProcess = NULL;
    299     bool fDefaultAllow = false;
    300     bool fInExceptionList = false;
    301 
    302     AssertPtrReturn(pfAllowed, VERR_INVALID_POINTER);
    303     AssertPtrReturn(puStartupDelay, VERR_INVALID_POINTER);
    304 
    305     *pfAllowed = false;
    306     *puStartupDelay = 0;
    307 
    308     rc = RTProcQueryUsernameA(RTProcSelf(), &pszUserProcess);
     646    PCFGAST pCfgAst = NULL;
     647
     648    AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
     649    AssertPtrReturn(ppCfgAst, VERR_INVALID_POINTER);
     650
     651    rc = autostartConfigTokenizerCreate(pszFilename, &pCfgTokenizer);
    309652    if (RT_SUCCESS(rc))
    310653    {
    311         PCFGTOKENIZER pCfgTokenizer = NULL;
    312 
    313         rc = autostartConfigTokenizerCreate(pszFilename, &pCfgTokenizer);
     654        rc = autostartConfigParseCompoundNode(pCfgTokenizer, "", &pCfgAst);
    314655        if (RT_SUCCESS(rc))
    315         {
    316             do
    317             {
    318                 size_t cchToken = 0;
    319                 const char *pszToken = NULL;
    320 
    321                 rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszToken,
    322                                                       &cchToken);
    323                 if (RT_SUCCESS(rc))
    324                 {
    325                     if (!RTStrNCmp(pszToken, "default_policy", strlen("default_policy")))
    326                     {
    327                         rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, "=");
    328                         if (RT_SUCCESS(rc))
    329                         {
    330                             rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszToken,
    331                                                                   &cchToken);
    332                             if (RT_SUCCESS(rc))
    333                             {
    334                                 if (!RTStrNCmp(pszToken, "allow", strlen("allow")))
    335                                     fDefaultAllow = true;
    336                                 else if (!RTStrNCmp(pszToken, "deny", strlen("deny")))
    337                                     fDefaultAllow = false;
    338                                 else
    339                                 {
    340                                     RTMsgError("Unexpected token at line %d, expected either 'allow' or 'deny'",
    341                                                pCfgTokenizer->iLine);
    342                                     rc = VERR_INVALID_PARAMETER;
    343                                     break;
    344                                 }
    345                             }
    346                         }
    347                     }
    348                     else if (!RTStrNCmp(pszToken, "exception_list", strlen("exception_list")))
    349                     {
    350                         rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, "=");
    351                         if (RT_SUCCESS(rc))
    352                         {
    353                             rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszToken,
    354                                                                   &cchToken);
    355 
    356                             while (RT_SUCCESS(rc))
    357                             {
    358                                 if (autostartConfigTokenizerIsReservedToken(pszToken, cchToken))
    359                                 {
    360                                     RTMsgError("Unexpected token at line %d, expected a username",
    361                                                pCfgTokenizer->iLine);
    362                                     rc = VERR_INVALID_PARAMETER;
    363                                     break;
    364                                 }
    365                                 else if (!RTStrNCmp(pszUserProcess, pszToken, strlen(pszUserProcess)))
    366                                     fInExceptionList = true;
    367 
    368                                 /* Skip , */
    369                                 rc = autostartConfigTokenizerPeek(pCfgTokenizer, &pszToken);
    370                                 if (   RT_SUCCESS(rc)
    371                                     && *pszToken == ',')
    372                                 {
    373                                     rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, ",");
    374                                     AssertRC(rc);
    375                                 }
    376                                 else if (RT_SUCCESS(rc))
    377                                     break;
    378 
    379                                 rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszToken,
    380                                                                       &cchToken);
    381                             }
    382 
    383                             if (rc == VERR_EOF)
    384                                 rc = VINF_SUCCESS;
    385                         }
    386                     }
    387                     else if (!autostartConfigTokenizerIsReservedToken(pszToken, cchToken))
    388                     {
    389                         /* Treat as 'username = <base delay in seconds>. */
    390                         rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, "=");
    391                         if (RT_SUCCESS(rc))
    392                         {
    393                             size_t cchDelay = 0;
    394                             const char *pszDelay = NULL;
    395 
    396                             rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszDelay,
    397                                                                   &cchDelay);
    398                             if (RT_SUCCESS(rc))
    399                             {
    400                                 uint32_t uDelay = 0;
    401 
    402                                 rc = RTStrToUInt32Ex(pszDelay, NULL, 10, &uDelay);
    403                                 if (rc == VWRN_TRAILING_SPACES)
    404                                     rc = VINF_SUCCESS;
    405 
    406                                 if (   RT_SUCCESS(rc)
    407                                     && !RTStrNCmp(pszUserProcess, pszToken, strlen(pszUserProcess)))
    408                                         *puStartupDelay = uDelay;
    409 
    410                                 if (RT_FAILURE(rc))
    411                                     RTMsgError("Unexpected token at line %d, expected a number",
    412                                                pCfgTokenizer->iLine);
    413                             }
    414                         }
    415                     }
    416                     else
    417                     {
    418                         RTMsgError("Unexpected token at line %d, expected a username",
    419                                    pCfgTokenizer->iLine);
    420                         rc = VERR_INVALID_PARAMETER;
    421                     }
    422                 }
    423                 else if (rc == VERR_EOF)
    424                 {
    425                     rc = VINF_SUCCESS;
    426                     break;
    427                 }
    428             } while (RT_SUCCESS(rc));
    429 
    430             if (   RT_SUCCESS(rc)
    431                 && (   (fDefaultAllow && !fInExceptionList)
    432                     || (!fDefaultAllow && fInExceptionList)))
    433                 *pfAllowed= true;
    434 
    435             autostartConfigTokenizerDestroy(pCfgTokenizer);
    436         }
    437 
    438         RTStrFree(pszUserProcess);
    439     }
     656            rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, CFGTOKENTYPE_EOF);
     657    }
     658
     659    if (pCfgTokenizer)
     660        autostartConfigTokenizerDestroy(pCfgTokenizer);
     661
     662    if (RT_SUCCESS(rc))
     663        *ppCfgAst = pCfgAst;
    440664
    441665    return rc;
    442666}
    443667
     668DECLHIDDEN(void) autostartConfigAstDestroy(PCFGAST pCfgAst)
     669{
     670    AssertPtrReturnVoid(pCfgAst);
     671
     672    switch (pCfgAst->enmType)
     673    {
     674        case CFGASTNODETYPE_KEYVALUE:
     675        {
     676            RTMemFree(pCfgAst);
     677            break;
     678        }
     679        case CFGASTNODETYPE_COMPOUND:
     680        {
     681            for (unsigned i = 0; i < pCfgAst->u.Compound.cAstNodes; i++)
     682                autostartConfigAstDestroy(pCfgAst->u.Compound.apAstNodes[i]);
     683            RTMemFree(pCfgAst);
     684            break;
     685        }
     686        case CFGASTNODETYPE_LIST:
     687        default:
     688            AssertMsgFailed(("Invalid AST node type %d\n", pCfgAst->enmType));
     689    }
     690}
     691
     692DECLHIDDEN(PCFGAST) autostartConfigAstGetByName(PCFGAST pCfgAst, const char *pszName)
     693{
     694    if (!pCfgAst)
     695        return NULL;
     696
     697    AssertReturn(pCfgAst->enmType == CFGASTNODETYPE_COMPOUND, NULL);
     698
     699    for (unsigned i = 0; i < pCfgAst->u.Compound.cAstNodes; i++)
     700    {
     701        PCFGAST pNode = pCfgAst->u.Compound.apAstNodes[i];
     702
     703        if (!RTStrCmp(pNode->pszKey, pszName))
     704            return pNode;
     705    }
     706
     707    return NULL;
     708}
     709
  • trunk/src/VBox/Frontends/VBoxAutostart/VBoxAutostartStart.cpp

    r42527 r42732  
    2323#include <VBox/com/errorprint.h>
    2424
     25#include <iprt/message.h>
    2526#include <iprt/thread.h>
    2627#include <iprt/stream.h>
     
    5152}
    5253
    53 DECLHIDDEN(RTEXITCODE) autostartStartMain(uint32_t uStartupDelay)
     54DECLHIDDEN(RTEXITCODE) autostartStartMain(PCFGAST pCfgAst)
    5455{
    5556    RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
    5657    int vrc = VINF_SUCCESS;
    5758    std::list<AUTOSTARTVM> listVM;
     59    uint32_t uStartupDelay = 0;
     60
     61    pCfgAst = autostartConfigAstGetByName(pCfgAst, "startup_delay");
     62    if (pCfgAst)
     63    {
     64        if (pCfgAst->enmType == CFGASTNODETYPE_KEYVALUE)
     65        {
     66            vrc = RTStrToUInt32Full(pCfgAst->u.KeyValue.aszValue, 10, &uStartupDelay);
     67            if (RT_FAILURE(vrc))
     68                return RTMsgErrorExit(RTEXITCODE_FAILURE, "'startup_delay' must be an unsigned number");
     69        }
     70    }
    5871
    5972    if (uStartupDelay)
  • trunk/src/VBox/Frontends/VBoxAutostart/VBoxAutostartStop.cpp

    r42527 r42732  
    3636using namespace com;
    3737
    38 DECLHIDDEN(RTEXITCODE) autostartStopMain(uint32_t uStartupDelay)
     38DECLHIDDEN(RTEXITCODE) autostartStopMain(PCFGAST pCfgAst)
    3939{
    4040    RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
Note: See TracChangeset for help on using the changeset viewer.

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