VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxAutostart/VBoxAutostartCfg.cpp@ 42533

Last change on this file since 42533 was 42527, checked in by vboxsync, 12 years ago

FE/VBoxAutostart: Refactoring

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.8 KB
Line 
1/* $Id: VBoxAutostartCfg.cpp 42527 2012-08-02 11:01:56Z vboxsync $ */
2/** @file
3 * VBoxAutostart - VirtualBox Autostart service, configuration parser.
4 */
5
6/*
7 * Copyright (C) 2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21
22#include <iprt/stream.h>
23#include <iprt/process.h>
24#include <iprt/string.h>
25#include <iprt/mem.h>
26#include <iprt/ctype.h>
27#include <iprt/message.h>
28
29#include "VBoxAutostart.h"
30
31/*******************************************************************************
32* Constants And Macros, Structures and Typedefs *
33*******************************************************************************/
34
35/**
36 * Tokenizer instance data for the config data.
37 */
38typedef struct CFGTOKENIZER
39{
40 /** Config file handle. */
41 PRTSTREAM hStrmConfig;
42 /** String buffer for the current line we are operating in. */
43 char *pszLine;
44 /** Size of the string buffer. */
45 size_t cbLine;
46 /** Current position in the line. */
47 char *pszLineCurr;
48 /** Current line in the config file. */
49 unsigned iLine;
50} CFGTOKENIZER, *PCFGTOKENIZER;
51
52/*******************************************************************************
53* Internal Functions *
54*******************************************************************************/
55
56/**
57 * Reads the next line from the config stream.
58 *
59 * @returns VBox status code.
60 * @param pCfgTokenizer The config tokenizer.
61 */
62static int autostartConfigTokenizerReadNextLine(PCFGTOKENIZER pCfgTokenizer)
63{
64 int rc = VINF_SUCCESS;
65
66 do
67 {
68 rc = RTStrmGetLine(pCfgTokenizer->hStrmConfig, pCfgTokenizer->pszLine,
69 pCfgTokenizer->cbLine);
70 if (rc == VERR_BUFFER_OVERFLOW)
71 {
72 char *pszTmp;
73
74 pCfgTokenizer->cbLine += 128;
75 pszTmp = (char *)RTMemRealloc(pCfgTokenizer->pszLine, pCfgTokenizer->cbLine);
76 if (pszTmp)
77 pCfgTokenizer->pszLine = pszTmp;
78 else
79 rc = VERR_NO_MEMORY;
80 }
81 } while (rc == VERR_BUFFER_OVERFLOW);
82
83 if (RT_SUCCESS(rc))
84 {
85 pCfgTokenizer->iLine++;
86 pCfgTokenizer->pszLineCurr = pCfgTokenizer->pszLine;
87 }
88
89 return rc;
90}
91
92/**
93 * Creates the config tokenizer from the given filename.
94 *
95 * @returns VBox status code.
96 * @param pszFilename Config filename.
97 * @param ppCfgTokenizer Where to store the pointer to the config tokenizer on
98 * success.
99 */
100static int autostartConfigTokenizerCreate(const char *pszFilename, PCFGTOKENIZER *ppCfgTokenizer)
101{
102 int rc = VINF_SUCCESS;
103 PCFGTOKENIZER pCfgTokenizer = (PCFGTOKENIZER)RTMemAllocZ(sizeof(CFGTOKENIZER));
104
105 if (pCfgTokenizer)
106 {
107 pCfgTokenizer->iLine = 1;
108 pCfgTokenizer->cbLine = 128;
109 pCfgTokenizer->pszLine = (char *)RTMemAllocZ(pCfgTokenizer->cbLine);
110 if (pCfgTokenizer->pszLine)
111 {
112 rc = RTStrmOpen(pszFilename, "r", &pCfgTokenizer->hStrmConfig);
113 if (RT_SUCCESS(rc))
114 rc = autostartConfigTokenizerReadNextLine(pCfgTokenizer);
115 }
116 else
117 rc = VERR_NO_MEMORY;
118 }
119 else
120 rc = VERR_NO_MEMORY;
121
122 if (RT_SUCCESS(rc))
123 *ppCfgTokenizer = pCfgTokenizer;
124 else if ( RT_FAILURE(rc)
125 && pCfgTokenizer)
126 {
127 if (pCfgTokenizer->pszLine)
128 RTMemFree(pCfgTokenizer->pszLine);
129 if (pCfgTokenizer->hStrmConfig)
130 RTStrmClose(pCfgTokenizer->hStrmConfig);
131 RTMemFree(pCfgTokenizer);
132 }
133
134 return rc;
135}
136
137/**
138 * Destroys the given config tokenizer.
139 *
140 * @returns nothing.
141 * @param pCfgTokenizer The config tokenizer to destroy.
142 */
143static 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.
154 *
155 * @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 */
161static int autostartConfigTokenizerReadNext(PCFGTOKENIZER pCfgTokenizer, const char **ppszToken,
162 size_t *pcchToken)
163{
164 if (!pCfgTokenizer->pszLineCurr)
165 return VERR_EOF;
166
167 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
214 return rc;
215}
216
217static 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.
238 *
239 * @returns VBox status code.
240 * @param pCfgTokenizer Tokenizer instance data.
241 * @param ppszTok Where to store the start of the next token on success.
242 */
243static int autostartConfigTokenizerPeek(PCFGTOKENIZER pCfgTokenizer, const char **ppszTok)
244{
245 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;
267 break;
268 }
269 }
270
271 return rc;
272}
273
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 */
281static 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
295DECLHIDDEN(int) autostartParseConfig(const char *pszFilename, bool *pfAllowed, uint32_t *puStartupDelay)
296{
297 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);
309 if (RT_SUCCESS(rc))
310 {
311 PCFGTOKENIZER pCfgTokenizer = NULL;
312
313 rc = autostartConfigTokenizerCreate(pszFilename, &pCfgTokenizer);
314 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 }
440
441 return rc;
442}
443
Note: See TracBrowser for help on using the repository browser.

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