VirtualBox

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

Last change on this file since 9734 was 9727, checked in by vboxsync, 16 years ago

HostServices: added a new service (SharedInfoService) to provide a host/guest configuration registry

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