VirtualBox

Changeset 42118 in vbox for trunk/src


Ignore:
Timestamp:
Jul 11, 2012 7:43:11 PM (13 years ago)
Author:
vboxsync
Message:

Autostart: Updates, implemented config parser for the autostart daemon

Location:
trunk/src/VBox
Files:
2 edited

Legend:

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

    r42001 r42118  
    5050#include <iprt/system.h>
    5151#include <iprt/time.h>
     52#include <iprt/ctype.h>
    5253
    5354
     
    6061
    6162/**
     63 * Tokenizer instance data for the config data.
     64 */
     65typedef struct CFGTOKENIZER
     66{
     67    /** Config file handle. */
     68    PRTSTREAM hStrmConfig;
     69    /** String buffer for the current line we are operating in. */
     70    char      *pszLine;
     71    /** Size of the string buffer. */
     72    size_t     cbLine;
     73    /** Current position in the line. */
     74    char      *pszLineCurr;
     75    /** Current line in the config file. */
     76    unsigned   iLine;
     77} CFGTOKENIZER, *PCFGTOKENIZER;
     78
     79/**
    6280 * VM list entry.
    6381 */
    6482typedef struct AUTOSTARTVM
    6583{
     84    /** ID of the VM to start. */
    6685    Bstr  strId;
     86    /** Startup delay of the VM. */
    6787    ULONG uStartupDelay;
    6888} AUTOSTARTVM;
     
    117137#define serviceLogVerbose(a) if (g_fVerbose) { serviceLog a; }
    118138
     139/**
     140 * Reads the next line from the config stream.
     141 *
     142 * @returns VBox status code.
     143 * @param   pCfgTokenizer    The config tokenizer.
     144 */
     145static int autostartConfigTokenizerReadNextLine(PCFGTOKENIZER pCfgTokenizer)
     146{
     147    int rc = VINF_SUCCESS;
     148
     149    do
     150    {
     151        rc = RTStrmGetLine(pCfgTokenizer->hStrmConfig, pCfgTokenizer->pszLine,
     152                           pCfgTokenizer->cbLine);
     153        if (rc == VERR_BUFFER_OVERFLOW)
     154        {
     155            char *pszTmp;
     156
     157            pCfgTokenizer->cbLine += 128;
     158            pszTmp = (char *)RTMemRealloc(pCfgTokenizer->pszLine, pCfgTokenizer->cbLine);
     159            if (pszTmp)
     160                pCfgTokenizer->pszLine = pszTmp;
     161            else
     162                rc = VERR_NO_MEMORY;
     163        }
     164    } while (rc == VERR_BUFFER_OVERFLOW);
     165
     166    if (RT_SUCCESS(rc))
     167    {
     168        pCfgTokenizer->iLine++;
     169        pCfgTokenizer->pszLineCurr = pCfgTokenizer->pszLine;
     170    }
     171
     172    return rc;
     173}
     174
     175/**
     176 * Creates the config tokenizer from the given filename.
     177 *
     178 * @returns VBox status code.
     179 * @param   pszFilename    Config filename.
     180 * @param   ppCfgTokenizer Where to store the pointer to the config tokenizer on
     181 *                         success.
     182 */
     183static int autostartConfigTokenizerCreate(const char *pszFilename, PCFGTOKENIZER *ppCfgTokenizer)
     184{
     185    int rc = VINF_SUCCESS;
     186    PCFGTOKENIZER pCfgTokenizer = (PCFGTOKENIZER)RTMemAllocZ(sizeof(CFGTOKENIZER));
     187
     188    if (pCfgTokenizer)
     189    {
     190        pCfgTokenizer->iLine = 1;
     191        pCfgTokenizer->cbLine = 128;
     192        pCfgTokenizer->pszLine = (char *)RTMemAllocZ(pCfgTokenizer->cbLine);
     193        if (pCfgTokenizer->pszLine)
     194        {
     195            rc = RTStrmOpen(pszFilename, "r", &pCfgTokenizer->hStrmConfig);
     196            if (RT_SUCCESS(rc))
     197                rc = autostartConfigTokenizerReadNextLine(pCfgTokenizer);
     198        }
     199        else
     200            rc = VERR_NO_MEMORY;
     201    }
     202    else
     203        rc = VERR_NO_MEMORY;
     204
     205    if (RT_SUCCESS(rc))
     206        *ppCfgTokenizer = pCfgTokenizer;
     207    else if (   RT_FAILURE(rc)
     208             && pCfgTokenizer)
     209    {
     210        if (pCfgTokenizer->pszLine)
     211            RTMemFree(pCfgTokenizer->pszLine);
     212        if (pCfgTokenizer->hStrmConfig)
     213            RTStrmClose(pCfgTokenizer->hStrmConfig);
     214        RTMemFree(pCfgTokenizer);
     215    }
     216
     217    return rc;
     218}
     219
     220/**
     221 * Destroys the given config tokenizer.
     222 *
     223 * @returns nothing.
     224 * @param   pCfgTokenizer    The config tokenizer to destroy.
     225 */
     226static void autostartConfigTokenizerDestroy(PCFGTOKENIZER pCfgTokenizer)
     227{
     228    if (pCfgTokenizer->pszLine)
     229        RTMemFree(pCfgTokenizer->pszLine);
     230    if (pCfgTokenizer->hStrmConfig)
     231        RTStrmClose(pCfgTokenizer->hStrmConfig);
     232    RTMemFree(pCfgTokenizer);
     233}
     234
     235/**
     236 * Read the next token from the config file.
     237 *
     238 * @returns VBox status code.
     239 * @param   pCfgTokenizer    The config tokenizer data.
     240 * @param   ppszToken        Where to store the start to the next token on success.
     241 * @param   pcchToken        Where to store the number of characters of the next token
     242 *                           excluding the \0 terminator on success.
     243 */
     244static int autostartConfigTokenizerReadNext(PCFGTOKENIZER pCfgTokenizer, const char **ppszToken,
     245                                            size_t *pcchToken)
     246{
     247    if (!pCfgTokenizer->pszLineCurr)
     248        return VERR_EOF;
     249
     250    int rc = VINF_SUCCESS;
     251
     252    for (;;)
     253    {
     254        char *pszTok = pCfgTokenizer->pszLineCurr;
     255
     256        /* Skip all spaces. */
     257        while (RT_C_IS_BLANK(*pszTok))
     258            pszTok++;
     259
     260        /* Check if we have to read a new line. */
     261        if (   *pszTok == '\0'
     262            || *pszTok == '#')
     263        {
     264            rc = autostartConfigTokenizerReadNextLine(pCfgTokenizer);
     265            if (RT_FAILURE(rc))
     266                break;
     267            /* start from the beginning. */
     268        }
     269        else if (   *pszTok == '='
     270                 || *pszTok == ',')
     271        {
     272            *ppszToken = pszTok;
     273            *pcchToken = 1;
     274            pCfgTokenizer->pszLineCurr = pszTok + 1;
     275            break;
     276        }
     277        else
     278        {
     279            /* Get the complete token. */
     280            size_t cchToken = 1;
     281            char *pszTmp = pszTok + 1;
     282
     283            while (   RT_C_IS_ALNUM(*pszTmp)
     284                   || *pszTmp == '_')
     285            {
     286                pszTmp++;
     287                cchToken++;
     288            }
     289
     290            *ppszToken = pszTok;
     291            *pcchToken = cchToken;
     292            pCfgTokenizer->pszLineCurr = pszTmp;
     293            break;
     294        }
     295    }
     296
     297    return rc;
     298}
     299
     300static int autostartConfigTokenizerCheckAndConsume(PCFGTOKENIZER pCfgTokenizer, const char *pszTokCheck)
     301{
     302    int rc = VINF_SUCCESS;
     303    const char *pszToken = NULL;
     304    size_t cchToken = 0;
     305
     306    rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszToken, &cchToken);
     307    if (RT_SUCCESS(rc))
     308    {
     309        if (RTStrNCmp(pszToken, pszTokCheck, cchToken))
     310        {
     311            RTMsgError("Unexpected token at line %d, expected '%s'",
     312                       pCfgTokenizer->iLine, pszTokCheck);
     313            rc = VERR_INVALID_PARAMETER;
     314        }
     315    }
     316    return rc;
     317}
     318
     319/**
     320 * Returns the start of the next token without consuming it.
     321 *
     322 * @returns VBox status code.
     323 * @param   pCfgTokenizer    Tokenizer instance data.
     324 * @param   ppszTok          Where to store the start of the next token on success.
     325 */
     326static int autostartConfigTokenizerPeek(PCFGTOKENIZER pCfgTokenizer, const char **ppszTok)
     327{
     328    int rc = VINF_SUCCESS;
     329
     330    for (;;)
     331    {
     332        char *pszTok = pCfgTokenizer->pszLineCurr;
     333
     334        /* Skip all spaces. */
     335        while (RT_C_IS_BLANK(*pszTok))
     336            pszTok++;
     337
     338        /* Check if we have to read a new line. */
     339        if (   *pszTok == '\0'
     340            || *pszTok == '#')
     341        {
     342            rc = autostartConfigTokenizerReadNextLine(pCfgTokenizer);
     343            if (RT_FAILURE(rc))
     344                break;
     345            /* start from the beginning. */
     346        }
     347        else
     348        {
     349            *ppszTok = pszTok;
     350            break;
     351        }
     352    }
     353
     354    return rc;
     355}
     356
     357/**
     358 * Check whether the given token is a reserved token.
     359 *
     360 * @returns true if the token is reserved or false otherwise.
     361 * @param   pszToken    The token to check.
     362 * @param   cchToken    Size of the token in characters.
     363 */
     364static bool autostartConfigTokenizerIsReservedToken(const char *pszToken, size_t cchToken)
     365{
     366    if (   cchToken == 1
     367        && (   *pszToken == ','
     368            || *pszToken == '='))
     369        return true;
     370    else if (   cchToken > 1
     371             && (   !RTStrNCmp(pszToken, "default_policy", cchToken)
     372                 || !RTStrNCmp(pszToken, "exception_list", cchToken)))
     373        return true;
     374
     375    return false;
     376}
     377
     378/**
     379 * Parse the given configuration file and return the interesting config parameters.
     380 *
     381 * @returns VBox status code.
     382 * @param   pszFilename    The config file to parse.
     383 * @param   pfAllowed      Where to store the flag whether the user of this process
     384 *                         is allowed to start VMs automatically during system startup.
     385 * @param   puStartupDelay Where to store the startup delay for the user.
     386 */
     387static int autostartParseConfig(const char *pszFilename, bool *pfAllowed, uint32_t *puStartupDelay)
     388{
     389    int rc = VINF_SUCCESS;
     390    char *pszUserProcess = NULL;
     391    bool fDefaultAllow = false;
     392    bool fInExceptionList = false;
     393
     394    AssertPtrReturn(pfAllowed, VERR_INVALID_POINTER);
     395    AssertPtrReturn(puStartupDelay, VERR_INVALID_POINTER);
     396
     397    *pfAllowed = false;
     398    *puStartupDelay = 0;
     399
     400    rc = RTProcQueryUsernameA(RTProcSelf(), &pszUserProcess);
     401    if (RT_SUCCESS(rc))
     402    {
     403        PCFGTOKENIZER pCfgTokenizer = NULL;
     404
     405        rc = autostartConfigTokenizerCreate(pszFilename, &pCfgTokenizer);
     406        if (RT_SUCCESS(rc))
     407        {
     408            do
     409            {
     410                size_t cchToken = 0;
     411                const char *pszToken = NULL;
     412
     413                rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszToken,
     414                                                      &cchToken);
     415                if (RT_SUCCESS(rc))
     416                {
     417                    if (!RTStrNCmp(pszToken, "default_policy", strlen("default_policy")))
     418                    {
     419                        rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, "=");
     420                        if (RT_SUCCESS(rc))
     421                        {
     422                            rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszToken,
     423                                                                  &cchToken);
     424                            if (RT_SUCCESS(rc))
     425                            {
     426                                if (!RTStrNCmp(pszToken, "allow", strlen("allow")))
     427                                    fDefaultAllow = true;
     428                                else if (!RTStrNCmp(pszToken, "deny", strlen("deny")))
     429                                    fDefaultAllow = false;
     430                                else
     431                                {
     432                                    RTMsgError("Unexpected token at line %d, expected either 'allow' or 'deny'",
     433                                               pCfgTokenizer->iLine);
     434                                    rc = VERR_INVALID_PARAMETER;
     435                                    break;
     436                                }
     437                            }
     438                        }
     439                    }
     440                    else if (!RTStrNCmp(pszToken, "exception_list", strlen("exception_list")))
     441                    {
     442                        rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, "=");
     443                        if (RT_SUCCESS(rc))
     444                        {
     445                            rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszToken,
     446                                                                  &cchToken);
     447
     448                            while (RT_SUCCESS(rc))
     449                            {
     450                                if (autostartConfigTokenizerIsReservedToken(pszToken, cchToken))
     451                                {
     452                                    RTMsgError("Unexpected token at line %d, expected a username",
     453                                               pCfgTokenizer->iLine);
     454                                    rc = VERR_INVALID_PARAMETER;
     455                                    break;
     456                                }
     457                                else if (!RTStrNCmp(pszUserProcess, pszToken, strlen(pszUserProcess)))
     458                                    fInExceptionList = true;
     459
     460                                /* Skip , */
     461                                rc = autostartConfigTokenizerPeek(pCfgTokenizer, &pszToken);
     462                                if (   RT_SUCCESS(rc)
     463                                    && *pszToken == ',')
     464                                {
     465                                    rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, ",");
     466                                    AssertRC(rc);
     467                                }
     468                                else if (RT_SUCCESS(rc))
     469                                    break;
     470
     471                                rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszToken,
     472                                                                      &cchToken);
     473                            }
     474
     475                            if (rc == VERR_EOF)
     476                                rc = VINF_SUCCESS;
     477                        }
     478                    }
     479                    else if (!autostartConfigTokenizerIsReservedToken(pszToken, cchToken))
     480                    {
     481                        /* Treat as 'username = <base delay in seconds>. */
     482                        rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, "=");
     483                        if (RT_SUCCESS(rc))
     484                        {
     485                            size_t cchDelay = 0;
     486                            const char *pszDelay = NULL;
     487
     488                            rc = autostartConfigTokenizerReadNext(pCfgTokenizer, &pszDelay,
     489                                                                  &cchDelay);
     490                            if (RT_SUCCESS(rc))
     491                            {
     492                                uint32_t uDelay = 0;
     493
     494                                rc = RTStrToUInt32Ex(pszDelay, NULL, 10, &uDelay);
     495                                if (rc == VWRN_TRAILING_SPACES)
     496                                    rc = VINF_SUCCESS;
     497
     498                                if (   RT_SUCCESS(rc)
     499                                    && !RTStrNCmp(pszUserProcess, pszToken, strlen(pszUserProcess)))
     500                                        *puStartupDelay = uDelay;
     501
     502                                if (RT_FAILURE(rc))
     503                                    RTMsgError("Unexpected token at line %d, expected a number",
     504                                               pCfgTokenizer->iLine);
     505                            }
     506                        }
     507                    }
     508                    else
     509                    {
     510                        RTMsgError("Unexpected token at line %d, expected a username",
     511                                   pCfgTokenizer->iLine);
     512                        rc = VERR_INVALID_PARAMETER;
     513                    }
     514                }
     515                else if (rc == VERR_EOF)
     516                {
     517                    rc = VINF_SUCCESS;
     518                    break;
     519                }
     520            } while (RT_SUCCESS(rc));
     521
     522            if (   RT_SUCCESS(rc)
     523                && (   (fDefaultAllow && !fInExceptionList)
     524                    || (!fDefaultAllow && fInExceptionList)))
     525                *pfAllowed= true;
     526
     527            autostartConfigTokenizerDestroy(pCfgTokenizer);
     528        }
     529
     530        RTStrFree(pszUserProcess);
     531    }
     532
     533    return rc;
     534}
     535
    119536static DECLCALLBACK(bool) autostartVMCmp(const AUTOSTARTVM &vm1, const AUTOSTARTVM &vm2)
    120537{
     
    127544 * @returns exit status code.
    128545 */
    129 static RTEXITCODE autostartMain(void)
     546static RTEXITCODE autostartMain(uint32_t uStartupDelay)
    130547{
    131548    RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
     549    int vrc = VINF_SUCCESS;
    132550    std::list<AUTOSTARTVM> listVM;
     551
     552    if (uStartupDelay)
     553    {
     554        serviceLogVerbose(("Delay starting for %d seconds ...\n", uStartupDelay));
     555        vrc = RTThreadSleep(uStartupDelay * 1000);
     556    }
     557
     558    if (vrc == VERR_INTERRUPTED)
     559        return RTEXITCODE_SUCCESS;
    133560
    134561    /*
     
    169596            && listVM.size())
    170597        {
     598            ULONG uDelayCurr = 0;
     599
    171600            /* Sort by startup delay and apply base override. */
    172601            listVM.sort(autostartVMCmp);
     
    178607                ComPtr<IProgress> progress;
    179608
     609                if ((*it).uStartupDelay > uDelayCurr)
     610                {
     611                    serviceLogVerbose(("Delay starting of the next VMs for %d seconds ...\n",
     612                                       (*it).uStartupDelay - uDelayCurr));
     613                    RTThreadSleep(((*it).uStartupDelay - uDelayCurr) * 1000);
     614                    uDelayCurr = (*it).uStartupDelay;
     615                }
     616
    180617                CHECK_ERROR_BREAK(g_pVirtualBox, FindMachine((*it).strId.raw(),
    181618                                                             machine.asOutParam()));
     
    185622                if (SUCCEEDED(rc) && !progress.isNull())
    186623                {
    187                     RTPrintf("Waiting for VM \"%ls\" to power on...\n", (*it).strId.raw());
     624                    serviceLogVerbose(("Waiting for VM \"%ls\" to power on...\n", (*it).strId.raw()));
    188625                    CHECK_ERROR(progress, WaitForCompletion(-1));
    189626                    if (SUCCEEDED(rc))
     
    205642                                }
    206643                                else
    207                                 {
    208                                     RTPrintf("VM \"%ls\" has been successfully started.\n", (*it).strId.raw());
    209                                 }
     644                                    serviceLogVerbose(("VM \"%ls\" has been successfully started.\n", (*it).strId.raw()));
    210645                            }
    211646                        }
     
    273708                break;
    274709#endif
    275             case 'P':
    276                 pcszDescr = "Name of the PID file which is created when the daemon was started.";
    277                 break;
    278710
    279711            case 'F':
     
    427859    }
    428860
     861    if (!pszConfigFile)
     862    {
     863        displayHelp(argv[0]);
     864        return RTMsgErrorExit(RTEXITCODE_FAILURE, "--config <config file> is missing");
     865    }
     866
    429867    if (!fQuiet)
    430868        displayHeader();
     869
     870    bool fAllowed = false;
     871    uint32_t uStartupDelay = 0;
     872    rc = autostartParseConfig(pszConfigFile, &fAllowed, &uStartupDelay);
     873    if (RT_FAILURE(rc))
     874        return RTEXITCODE_FAILURE;
     875
     876    if (!fAllowed)
     877        return RTMsgErrorExit(RTEXITCODE_FAILURE, "User is not allowed to autostart VMs");
    431878
    432879    /* create release logger, to stdout */
     
    509956        return RTEXITCODE_FAILURE;
    510957
    511     RTEXITCODE rcExit = autostartMain();
     958    RTEXITCODE rcExit = autostartMain(uStartupDelay);
    512959
    513960    EventQueue::getMainEventQueue()->processEventQueue(0);
  • trunk/src/VBox/Main/src-server/generic/AutostartDb-generic.cpp

    r42069 r42118  
    5454        if (fAddVM)
    5555            fOpen |= RTFILE_O_OPEN_CREATE;
     56        else
     57            fOpen |= RTFILE_O_OPEN;
    5658
    5759        rc = RTStrAPrintf(&pszFile, "%s/%s.%s",
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