VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedInfoServices/service.cpp@ 10719

Last change on this file since 10719 was 10144, checked in by vboxsync, 16 years ago

HostServices/SharedInfoServices: made the guest property functions take utf8 keys and values instead of ASCII without spaces

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 17.5 KB
Line 
1/** @file
2 *
3 * Shared Information Services:
4 * Host service entry points.
5 */
6
7/*
8 * Copyright (C) 2008 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23/**
24 * An HGCM service for passing requests which do not need any persistant state
25 * to handle. We currently only support three types of request - set guest
26 * property (SET_CONFIG_KEY and SET_CONFIG_KEY_HOST), get guest property
27 * (GET_CONFIG_KEY and GET_CONFIG_KEY_HOST) and remove guest property
28 * (DEL_CONFIG_KEY and DEL_CONFIG_KEY_HOST). These may be used to read, to
29 * write and to remove configuration information which is available to
30 * both guest and host. This configuration information is stored in a CFGM
31 * node using the CFGM APIs. It is the responsibility of whoever creates the
32 * service to create this node and to tell the service about it using the
33 * SET_CFGM_NODE host call. It is also the responsibility of the service
34 * creator to save this information to disk and to retrieve it when needed.
35 * Since the CFGM APIs are single threaded, the creator must also ensure that
36 * no-one else accesses the configuration node while the service is running.
37 *
38 * If this service is extended to deal with new requests it would probably be a
39 * good idea to split it up into several files.
40 */
41
42#define LOG_GROUP LOG_GROUP_HGCM
43
44/*******************************************************************************
45* Header Files *
46*******************************************************************************/
47#include <VBox/HostServices/VBoxInfoSvc.h>
48
49#include <memory> /* for auto_ptr */
50
51#include <iprt/err.h>
52#include <iprt/assert.h>
53#include <iprt/string.h>
54#include <VBox/log.h>
55
56#include <VBox/cfgm.h>
57
58#include "noncopyable.h"
59
60/*******************************************************************************
61* Internal functions *
62*******************************************************************************/
63/** Extract a pointer value from an HGCM parameter structure */
64static int VBoxHGCMParmPtrGet (VBOXHGCMSVCPARM *pParm, void **ppv, uint32_t *pcb)
65{
66 if (pParm->type == VBOX_HGCM_SVC_PARM_PTR)
67 {
68 *ppv = pParm->u.pointer.addr;
69 *pcb = pParm->u.pointer.size;
70 return VINF_SUCCESS;
71 }
72
73 return VERR_INVALID_PARAMETER;
74}
75
76/** Set a uint32_t value to an HGCM parameter structure */
77static void VBoxHGCMParmUInt32Set (VBOXHGCMSVCPARM *pParm, uint32_t u32)
78{
79 pParm->type = VBOX_HGCM_SVC_PARM_32BIT;
80 pParm->u.uint32 = u32;
81}
82
83
84namespace svcInfo {
85
86/**
87 * Class containing the shared information service functionality.
88 */
89class Service : public noncopyable
90{
91private:
92 /** Type definition for use in callback functions */
93 typedef Service SELF;
94 /** HGCM helper functions. */
95 PVBOXHGCMSVCHELPERS mpHelpers;
96 /** Pointer to our configuration node. */
97 PCFGMNODE mpNode;
98
99public:
100 explicit Service(PVBOXHGCMSVCHELPERS pHelpers)
101 : mpHelpers(pHelpers), mpNode(NULL) {}
102
103 /**
104 * @copydoc VBOXHGCMSVCHELPERS::pfnUnload
105 * Simply deletes the service object
106 */
107 static DECLCALLBACK(int) svcUnload (void *pvService)
108 {
109 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
110 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
111 delete pSelf;
112 return VINF_SUCCESS;
113 }
114
115 /**
116 * @copydoc VBOXHGCMSVCHELPERS::pfnConnect
117 * Stub implementation of pfnConnect and pfnDisconnect.
118 */
119 static DECLCALLBACK(int) svcConnectDisconnect (void * /* pvService */,
120 uint32_t /* u32ClientID */,
121 void * /* pvClient */)
122 {
123 return VINF_SUCCESS;
124 }
125
126 /**
127 * @copydoc VBOXHGCMSVCHELPERS::pfnCall
128 * Wraps to the call member function
129 */
130 static DECLCALLBACK(void) svcCall (void * pvService,
131 VBOXHGCMCALLHANDLE callHandle,
132 uint32_t u32ClientID,
133 void *pvClient,
134 uint32_t u32Function,
135 uint32_t cParms,
136 VBOXHGCMSVCPARM paParms[])
137 {
138 AssertLogRelReturnVoid(VALID_PTR(pvService));
139 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
140 pSelf->call(callHandle, u32ClientID, pvClient, u32Function, cParms, paParms);
141 }
142
143 /**
144 * @copydoc VBOXHGCMSVCHELPERS::pfnHostCall
145 * Wraps to the hostCall member function
146 */
147 static DECLCALLBACK(int) svcHostCall (void *pvService,
148 uint32_t u32Function,
149 uint32_t cParms,
150 VBOXHGCMSVCPARM paParms[])
151 {
152 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
153 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
154 return pSelf->hostCall(u32Function, cParms, paParms);
155 }
156private:
157 int validateKey(const char *pszKey, uint32_t cbKey);
158 int validateValue(char *pszValue, uint32_t cbValue);
159 int getKey(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
160 int setKey(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
161 int delKey(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
162 void call (VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
163 void *pvClient, uint32_t eFunction, uint32_t cParms,
164 VBOXHGCMSVCPARM paParms[]);
165 int hostCall (uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
166};
167
168
169/**
170 * Checking that the key passed by the guest fits our criteria for a
171 * configuration key
172 *
173 * @returns IPRT status code
174 * @param pszKey the key passed by the guest
175 * @param cbKey the number of bytes pszKey points to, including the
176 * terminating '\0'
177 * @thread HGCM
178 */
179int Service::validateKey(const char *pszKey, uint32_t cbKey)
180{
181 LogFlowFunc(("cbKey=%d\n", cbKey));
182
183 size_t count;
184 int rc = VINF_SUCCESS;
185
186 /* Validate the format of the key. */
187 /* Only accept names in valid Utf8. */
188 rc = RTStrValidateEncodingEx(pszKey, cbKey, 0);
189 if (RT_SUCCESS(rc))
190 /* We want the byte length, not the Utf8 length */
191 count = strlen(pszKey);
192 if (RT_SUCCESS(rc) && (count > KEY_MAX_LEN))
193 rc = VERR_INVALID_PARAMETER;
194
195 LogFlowFunc(("returning %Rrc\n", rc));
196 return rc;
197}
198
199
200/**
201 * Check that the data passed by the guest fits our criteria for the value of
202 * a configuration key
203 *
204 * @returns IPRT status code
205 * @param pszValue the value to store in the key
206 * @param cbValue the number of bytes in the buffer pszValue points to
207 * @thread HGCM
208 */
209int Service::validateValue(char *pszValue, uint32_t cbValue)
210{
211 LogFlowFunc(("cbValue=%d\n", cbValue));
212
213 size_t count;
214 int rc = VINF_SUCCESS;
215
216 /* Validate the format of the value. */
217 /* Only accept values in valid Utf8 */
218 rc = RTStrValidateEncodingEx(pszValue, cbValue, 0);
219 if (RT_SUCCESS(rc))
220 /* We want the byte length, not the Utf8 length */
221 count = strlen(pszValue);
222 if (RT_SUCCESS(rc) && (count > KEY_MAX_VALUE_LEN))
223 rc = VERR_INVALID_PARAMETER;
224
225 if (RT_SUCCESS(rc))
226 LogFlow((" pszValue=%s\n", cbValue > 0 ? pszValue : NULL));
227 LogFlowFunc(("returning %Rrc\n", rc));
228 return rc;
229}
230
231
232/**
233 * Retrieve a value from the guest registry by key, checking the validity
234 * of the arguments passed. If the guest has not allocated enough buffer
235 * space for the value then we return VERR_OVERFLOW and set the size of the
236 * buffer needed in the "size" HGCM parameter. If the key was not found at
237 * all, we return VERR_NOT_FOUND.
238 *
239 * @returns iprt status value
240 * @param cParms the number of HGCM parameters supplied
241 * @param paParms the array of HGCM parameters
242 * @thread HGCM
243 */
244int Service::getKey(uint32_t cParms, VBOXHGCMSVCPARM paParms[])
245{
246 int rc = VINF_SUCCESS;
247 char *pszKey, *pszValue;
248 uint32_t cbKey, cbValue;
249 size_t cbValueActual;
250
251 LogFlowThisFunc(("\n"));
252 if ( (cParms != 3) /* Hardcoded value as the next lines depend on it. */
253 || (paParms[0].type != VBOX_HGCM_SVC_PARM_PTR) /* key */
254 || (paParms[1].type != VBOX_HGCM_SVC_PARM_PTR) /* value */
255 )
256 rc = VERR_INVALID_PARAMETER;
257 if (RT_SUCCESS(rc))
258 rc = VBoxHGCMParmPtrGet(&paParms[0], (void **) &pszKey, &cbKey);
259 if (RT_SUCCESS(rc))
260 rc = VBoxHGCMParmPtrGet(&paParms[1], (void **) &pszValue, &cbValue);
261 if (RT_SUCCESS(rc))
262 rc = validateKey(pszKey, cbKey);
263 if (RT_SUCCESS(rc))
264 rc = CFGMR3QuerySize(mpNode, pszKey, &cbValueActual);
265 if (RT_SUCCESS(rc))
266 VBoxHGCMParmUInt32Set(&paParms[2], cbValueActual);
267 if (RT_SUCCESS(rc) && (cbValueActual > cbValue))
268 rc = VERR_BUFFER_OVERFLOW;
269 if (RT_SUCCESS(rc) && (rc != VINF_BUFFER_OVERFLOW))
270 rc = CFGMR3QueryString(mpNode, pszKey, pszValue, cbValue);
271 if (RT_SUCCESS(rc) && (rc != VINF_BUFFER_OVERFLOW))
272 Log2(("Queried string %s, rc=%Rrc, value=%.*s\n", pszKey, rc, cbValue, pszValue));
273 else if (VERR_CFGM_VALUE_NOT_FOUND == rc)
274 rc = VERR_NOT_FOUND;
275 LogFlowThisFunc(("rc = %Rrc\n", rc));
276 return rc;
277}
278
279
280/**
281 * Set a value in the guest registry by key, checking the validity
282 * of the arguments passed.
283 *
284 * @returns iprt status value
285 * @param cParms the number of HGCM parameters supplied
286 * @param paParms the array of HGCM parameters
287 * @thread HGCM
288 */
289int Service::setKey(uint32_t cParms, VBOXHGCMSVCPARM paParms[])
290{
291 int rc = VINF_SUCCESS;
292 char *pszKey, *pszValue;
293 uint32_t cbKey, cbValue;
294
295 LogFlowThisFunc(("\n"));
296 if ( (cParms != 2) /* Hardcoded value as the next lines depend on it. */
297 || (paParms[0].type != VBOX_HGCM_SVC_PARM_PTR) /* key */
298 || (paParms[1].type != VBOX_HGCM_SVC_PARM_PTR) /* value */
299 )
300 rc = VERR_INVALID_PARAMETER;
301 if (RT_SUCCESS(rc))
302 rc = VBoxHGCMParmPtrGet(&paParms[0], (void **) &pszKey, &cbKey);
303 if (RT_SUCCESS(rc))
304 rc = VBoxHGCMParmPtrGet(&paParms[1], (void **) &pszValue, &cbValue);
305 if (RT_SUCCESS(rc))
306 rc = validateKey(pszKey, cbKey);
307 if (RT_SUCCESS(rc))
308 rc = validateValue(pszValue, cbValue);
309 if (RT_SUCCESS(rc))
310 {
311 /* Limit the number of keys that we can set. */
312 unsigned cChildren = 0;
313 for (PCFGMNODE pChild = CFGMR3GetFirstChild(mpNode); pChild != 0; pChild = CFGMR3GetNextChild(pChild))
314 ++cChildren;
315 if (cChildren >= KEY_MAX_KEYS)
316 rc = VERR_TOO_MUCH_DATA;
317 }
318 if (RT_SUCCESS(rc))
319 {
320 CFGMR3RemoveValue(mpNode, pszKey);
321 if (pszValue > 0)
322 rc = CFGMR3InsertString(mpNode, pszKey, pszValue);
323 }
324 if (RT_SUCCESS(rc))
325 Log2(("Set string %s, rc=%Rrc, value=%s\n", pszKey, rc, pszValue));
326 LogFlowThisFunc(("rc = %Rrc\n", rc));
327 return rc;
328}
329
330
331/**
332 * Remove a value in the guest registry by key, checking the validity
333 * of the arguments passed.
334 *
335 * @returns iprt status value
336 * @param cParms the number of HGCM parameters supplied
337 * @param paParms the array of HGCM parameters
338 * @thread HGCM
339 */
340int Service::delKey(uint32_t cParms, VBOXHGCMSVCPARM paParms[])
341{
342 int rc = VINF_SUCCESS;
343 char *pszKey;
344 uint32_t cbKey;
345
346 LogFlowThisFunc(("\n"));
347 if ( (cParms != 1) /* Hardcoded value as the next lines depend on it. */
348 || (paParms[0].type != VBOX_HGCM_SVC_PARM_PTR) /* key */
349 )
350 rc = VERR_INVALID_PARAMETER;
351 if (RT_SUCCESS(rc))
352 rc = VBoxHGCMParmPtrGet(&paParms[0], (void **) &pszKey, &cbKey);
353 if (RT_SUCCESS(rc))
354 rc = validateKey(pszKey, cbKey);
355 if (RT_SUCCESS(rc))
356 CFGMR3RemoveValue(mpNode, pszKey);
357 LogFlowThisFunc(("rc = %Rrc\n", rc));
358 return rc;
359}
360
361
362/**
363 * Handle an HGCM service call.
364 * @copydoc VBOXHGCMSVCFNTABLE::pfnCall
365 * @note All functions which do not involve an unreasonable delay will be
366 * handled synchronously. If needed, we will add a request handler
367 * thread in future for those which do.
368 *
369 * @thread HGCM
370 */
371void Service::call (VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
372 void * /* pvClient */, uint32_t eFunction, uint32_t cParms,
373 VBOXHGCMSVCPARM paParms[])
374{
375 int rc = VINF_SUCCESS;
376 bool fCallSync = true;
377
378 LogFlowFunc(("u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n",
379 u32ClientID, eFunction, cParms, paParms));
380
381 switch (eFunction)
382 {
383 /* The guest wishes to read a configuration value */
384 case GET_CONFIG_KEY:
385 LogFlowFunc(("GET_CONFIG_KEY\n"));
386 rc = getKey(cParms, paParms);
387 break;
388
389 /* The guest wishes to set a configuration value */
390 case SET_CONFIG_KEY:
391 LogFlowFunc(("SET_CONFIG_KEY\n"));
392 rc = setKey(cParms, paParms);
393 break;
394
395 /* The guest wishes to remove a configuration value */
396 case DEL_CONFIG_KEY:
397 LogFlowFunc(("DEL_CONFIG_KEY\n"));
398 rc = delKey(cParms, paParms);
399 break;
400
401 default:
402 rc = VERR_NOT_IMPLEMENTED;
403 }
404 if (fCallSync)
405 {
406 LogFlowFunc(("rc = %Rrc\n", rc));
407 mpHelpers->pfnCallComplete (callHandle, rc);
408 }
409}
410
411
412/**
413 * Service call handler for the host.
414 * @copydoc VBOXHGCMSVCFNTABLE::pfnHostCall
415 * @thread hgcm
416 */
417int Service::hostCall (uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
418{
419 int rc = VINF_SUCCESS;
420
421 LogFlowFunc(("fn = %d, cParms = %d, pparms = %d\n",
422 eFunction, cParms, paParms));
423
424 switch (eFunction)
425 {
426 /* Set the root CFGM node used. This should be called when instantiating
427 * the service. */
428 case SET_CFGM_NODE:
429 {
430 LogFlowFunc(("SET_CFGM_NODE\n"));
431
432 if (cParms != 1)
433 {
434 rc = VERR_INVALID_PARAMETER;
435 }
436 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* pNode */
437 )
438 {
439 rc = VERR_INVALID_PARAMETER;
440 }
441 else
442 {
443 PCFGMNODE pNode = NULL;
444 uint32_t cbDummy;
445
446 rc = VBoxHGCMParmPtrGet (&paParms[0], (void **) &pNode, &cbDummy);
447 mpNode = pNode;
448 }
449 } break;
450
451 /* The host wishes to read a configuration value */
452 case GET_CONFIG_KEY_HOST:
453 LogFlowFunc(("GET_CONFIG_KEY_HOST\n"));
454 rc = getKey(cParms, paParms);
455 break;
456
457 /* The host wishes to set a configuration value */
458 case SET_CONFIG_KEY_HOST:
459 LogFlowFunc(("SET_CONFIG_KEY_HOST\n"));
460 rc = setKey(cParms, paParms);
461 break;
462
463 /* The host wishes to remove a configuration value */
464 case DEL_CONFIG_KEY_HOST:
465 LogFlowFunc(("DEL_CONFIG_KEY_HOST\n"));
466 rc = delKey(cParms, paParms);
467 break;
468
469 default:
470 rc = VERR_NOT_SUPPORTED;
471 break;
472 }
473
474 LogFlowFunc(("rc = %Vrc\n", rc));
475 return rc;
476}
477
478} /* namespace svcInfo */
479
480using svcInfo::Service;
481
482/**
483 * @copydoc VBOXHGCMSVCLOAD
484 */
485extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable)
486{
487 int rc = VINF_SUCCESS;
488
489 LogFlowFunc(("ptable = %p\n", ptable));
490
491 if (!VALID_PTR(ptable))
492 {
493 rc = VERR_INVALID_PARAMETER;
494 }
495 else
496 {
497 LogFlowFunc(("ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
498
499 if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
500 || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
501 {
502 rc = VERR_VERSION_MISMATCH;
503 }
504 else
505 {
506 std::auto_ptr<Service> apService;
507 try {
508 apService = std::auto_ptr<Service>(new Service(ptable->pHelpers));
509 } catch (...) {
510 /* No exceptions may propogate outside. */
511 rc = VERR_UNRESOLVED_ERROR;
512 }
513
514 if (RT_SUCCESS(rc))
515 {
516 /* We do not maintain connections, so no client data is needed. */
517 ptable->cbClient = 0;
518
519 ptable->pfnUnload = Service::svcUnload;
520 ptable->pfnConnect = Service::svcConnectDisconnect;
521 ptable->pfnDisconnect = Service::svcConnectDisconnect;
522 ptable->pfnCall = Service::svcCall;
523 ptable->pfnHostCall = Service::svcHostCall;
524 ptable->pfnSaveState = NULL; /* The service is stateless by definition, so the */
525 ptable->pfnLoadState = NULL; /* normal construction done before restoring suffices */
526 ptable->pfnRegisterExtension = NULL;
527
528 /* Service specific initialization. */
529 ptable->pvService = apService.release();
530 }
531 }
532 }
533
534 LogFlowFunc(("returning %Rrc\n", rc));
535 return rc;
536}
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