VirtualBox

source: vbox/trunk/src/VBox/HostServices/GuestControl/service.cpp@ 75801

Last change on this file since 75801 was 75801, checked in by vboxsync, 6 years ago

VBoxGuestControl: Optimizing message handling - part 2. bugref:9313

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 83.5 KB
Line 
1/* $Id: service.cpp 75801 2018-11-29 01:54:46Z vboxsync $ */
2/** @file
3 * Guest Control Service: Controlling the guest.
4 */
5
6/*
7 * Copyright (C) 2011-2018 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/** @page pg_svc_guest_control Guest Control HGCM Service
19 *
20 * This service acts as a proxy for handling and buffering host command requests
21 * and clients on the guest. It tries to be as transparent as possible to let
22 * the guest (client) and host side do their protocol handling as desired.
23 *
24 * The following terms are used:
25 * - Host: A host process (e.g. VBoxManage or another tool utilizing the Main API)
26 * which wants to control something on the guest.
27 * - Client: A client (e.g. VBoxService) running inside the guest OS waiting for
28 * new host commands to perform. There can be multiple clients connected
29 * to this service. A client is represented by its unique HGCM client ID.
30 * - Context ID: An (almost) unique ID automatically generated on the host (Main API)
31 * to not only distinguish clients but individual requests. Because
32 * the host does not know anything about connected clients it needs
33 * an indicator which it can refer to later. This context ID gets
34 * internally bound by the service to a client which actually processes
35 * the command in order to have a relationship between client<->context ID(s).
36 *
37 * The host can trigger commands which get buffered by the service (with full HGCM
38 * parameter info). As soon as a client connects (or is ready to do some new work)
39 * it gets a buffered host command to process it. This command then will be immediately
40 * removed from the command list. If there are ready clients but no new commands to be
41 * processed, these clients will be set into a deferred state (that is being blocked
42 * to return until a new command is available).
43 *
44 * If a client needs to inform the host that something happened, it can send a
45 * message to a low level HGCM callback registered in Main. This callback contains
46 * the actual data as well as the context ID to let the host do the next necessary
47 * steps for this context. This context ID makes it possible to wait for an event
48 * inside the host's Main API function (like starting a process on the guest and
49 * wait for getting its PID returned by the client) as well as cancelling blocking
50 * host calls in order the client terminated/crashed (HGCM detects disconnected
51 * clients and reports it to this service's callback).
52 *
53 * Starting at VBox 4.2 the context ID itself consists of a session ID, an object
54 * ID (for example a process or file ID) and a count. This is necessary to not break
55 * compatibility between older hosts and to manage guest session on the host.
56 */
57
58
59/*********************************************************************************************************************************
60* Header Files *
61*********************************************************************************************************************************/
62#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
63#include <VBox/HostServices/GuestControlSvc.h>
64
65#include <VBox/log.h>
66#include <VBox/AssertGuest.h>
67#include <VBox/VMMDev.h>
68#include <iprt/assert.h>
69#include <iprt/cpp/autores.h>
70#include <iprt/cpp/utils.h>
71#include <iprt/err.h>
72#include <iprt/mem.h>
73#include <iprt/list.h>
74#include <iprt/req.h>
75#include <iprt/string.h>
76#include <iprt/thread.h>
77#include <iprt/time.h>
78
79#include <map>
80#include <new> /* for std::nothrow*/
81#include <string>
82#include <list>
83
84
85using namespace guestControl;
86
87
88/**
89 * Structure for maintaining a pending (that is, a deferred and not yet completed)
90 * client command.
91 */
92typedef struct ClientConnection
93{
94 /** The call handle */
95 VBOXHGCMCALLHANDLE mHandle;
96 /** Number of parameters */
97 uint32_t mNumParms;
98 /** The call parameters */
99 VBOXHGCMSVCPARM *mParms;
100 /** The standard constructor. */
101 ClientConnection(void)
102 : mHandle(0), mNumParms(0), mParms(NULL) {}
103} ClientConnection;
104
105/**
106 * Structure for holding a buffered host command which has
107 * not been processed yet.
108 */
109typedef struct HostCommand
110{
111 /** Entry on the GstCtrlService::mHostCmdList list. */
112 RTLISTNODE m_ListEntry;
113 /** Reference counter for facilitating sending to both session and root. */
114 uint32_t m_cRefs;
115 union
116 {
117 /** The top two twomost bits are exploited for message destination.
118 * See VBOX_GUESTCTRL_DST_XXX. */
119 uint64_t m_idContextAndDst;
120 /** The context ID this command belongs to (extracted from the first parameter). */
121 uint32_t m_idContext;
122 };
123 /** Dynamic structure for holding the HGCM parms */
124 uint32_t mMsgType;
125 /** Number of HGCM parameters. */
126 uint32_t mParmCount;
127 /** Array of HGCM parameters. */
128 PVBOXHGCMSVCPARM mpParms;
129
130 HostCommand()
131 : m_cRefs(1)
132 , m_idContextAndDst(0)
133 , mMsgType(UINT32_MAX)
134 , mParmCount(0)
135 , mpParms(NULL)
136 {
137 RTListInit(&m_ListEntry);
138 }
139
140 /**
141 * Retains a reference to the command.
142 */
143 uint32_t Retain(void)
144 {
145 uint32_t cRefs = ++m_cRefs;
146 Log4(("[Cmd %RU32 (%s)] Adding reference, new m_cRefs=%u\n", mMsgType, GstCtrlHostFnName((eHostFn)mMsgType), cRefs));
147 Assert(cRefs < 4);
148 return cRefs;
149 }
150
151 /**
152 * Releases the host command, properly deleting it if no further references.
153 */
154 uint32_t SaneRelease(void)
155 {
156 uint32_t cRefs = --m_cRefs;
157 Log4(("[Cmd %RU32] sane release - cRefs=%u\n", mMsgType, cRefs));
158 Assert(cRefs < 4);
159
160 if (!cRefs)
161 {
162 LogFlowThisFunc(("[Cmd %RU32 (%s)] destroying\n", mMsgType, GstCtrlHostFnName((eHostFn)mMsgType)));
163 RTListNodeRemove(&m_ListEntry);
164 if (mpParms)
165 {
166 for (uint32_t i = 0; i < mParmCount; i++)
167 if (mpParms[i].type == VBOX_HGCM_SVC_PARM_PTR)
168 {
169 RTMemFree(mpParms[i].u.pointer.addr);
170 mpParms[i].u.pointer.addr = NULL;
171 }
172 RTMemFree(mpParms);
173 mpParms = NULL;
174 }
175 mParmCount = 0;
176 delete this;
177 }
178 return cRefs;
179 }
180
181 /**
182 * Initializes the command.
183 *
184 * The specified parameters are copied and any buffers referenced by it
185 * duplicated as well.
186 *
187 * @return IPRT status code.
188 * @param idFunction The host function (message) number, eHostFn.
189 * @param cParms Number of parameters in the HGCM request.
190 * @param paParms Array of parameters.
191 */
192 int Init(uint32_t idFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
193 {
194 LogFlowThisFunc(("[Cmd %RU32 (%s)] Allocating cParms=%RU32, paParms=%p\n",
195 idFunction, GstCtrlHostFnName((eHostFn)idFunction), cParms, paParms));
196 Assert(mpParms == NULL);
197 Assert(mParmCount == 0);
198 Assert(m_cRefs == 1);
199
200 /*
201 * Fend of bad stuff.
202 */
203 AssertReturn(cParms > 0, VERR_WRONG_PARAMETER_COUNT); /* At least one parameter (context ID) must be present. */
204 AssertReturn(cParms < VMMDEV_MAX_HGCM_PARMS, VERR_WRONG_PARAMETER_COUNT);
205 AssertPtrReturn(paParms, VERR_INVALID_POINTER);
206
207 /*
208 * The first parameter is the context ID and the command destiation mask.
209 */
210 if (paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT)
211 {
212 m_idContextAndDst = paParms[0].u.uint64;
213 AssertReturn(m_idContextAndDst & VBOX_GUESTCTRL_DST_BOTH, VERR_INTERNAL_ERROR_3);
214 }
215 else if (paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT)
216 {
217 AssertMsgFailed(("idFunction=%u %s - caller must set dst!\n", idFunction, GstCtrlHostFnName((eHostFn)idFunction)));
218 m_idContextAndDst = paParms[0].u.uint32 | VBOX_GUESTCTRL_DST_BOTH;
219 }
220 else
221 AssertFailedReturn(VERR_WRONG_PARAMETER_TYPE);
222
223 /*
224 * Just make a copy of the parameters and any buffers.
225 */
226 mMsgType = idFunction;
227 mParmCount = cParms;
228 mpParms = (VBOXHGCMSVCPARM *)RTMemAllocZ(sizeof(VBOXHGCMSVCPARM) * mParmCount);
229 AssertReturn(mpParms, VERR_NO_MEMORY);
230
231 for (uint32_t i = 0; i < cParms; i++)
232 {
233 mpParms[i].type = paParms[i].type;
234 switch (paParms[i].type)
235 {
236 case VBOX_HGCM_SVC_PARM_32BIT:
237 mpParms[i].u.uint32 = paParms[i].u.uint32;
238 break;
239
240 case VBOX_HGCM_SVC_PARM_64BIT:
241 mpParms[i].u.uint64 = paParms[i].u.uint64;
242 break;
243
244 case VBOX_HGCM_SVC_PARM_PTR:
245 mpParms[i].u.pointer.size = paParms[i].u.pointer.size;
246 if (mpParms[i].u.pointer.size > 0)
247 {
248 mpParms[i].u.pointer.addr = RTMemDup(paParms[i].u.pointer.addr, mpParms[i].u.pointer.size);
249 AssertReturn(mpParms[i].u.pointer.addr, VERR_NO_MEMORY);
250 }
251 /* else: structure is zeroed by allocator. */
252 break;
253
254 default:
255 AssertMsgFailedReturn(("idFunction=%u (%s) parameter #%u: type=%u\n",
256 idFunction, GstCtrlHostFnName((eHostFn)idFunction), i, paParms[i].type),
257 VERR_WRONG_PARAMETER_TYPE);
258 }
259 }
260
261 /*
262 * Morph the first parameter back to 32-bit.
263 */
264 mpParms[0].type = VBOX_HGCM_SVC_PARM_32BIT;
265 mpParms[0].u.uint32 = (uint32_t)paParms[0].u.uint64;
266
267 return VINF_SUCCESS;
268 }
269
270
271 /**
272 * Worker for Assign() that opies data from the buffered HGCM request to the
273 * current HGCM request.
274 *
275 * @return IPRT status code.
276 * @param paDstParms Array of parameters of HGCM request to fill the data into.
277 * @param cDstParms Number of parameters the HGCM request can handle.
278 */
279 int CopyTo(VBOXHGCMSVCPARM paDstParms[], uint32_t cDstParms) const
280 {
281 LogFlowThisFunc(("[Cmd %RU32] mParmCount=%RU32, m_idContext=%RU32 (Session %RU32)\n",
282 mMsgType, mParmCount, m_idContext, VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(m_idContext)));
283
284 int rc = VINF_SUCCESS;
285 if (cDstParms != mParmCount)
286 {
287 LogFlowFunc(("Parameter count does not match (got %RU32, expected %RU32)\n",
288 cDstParms, mParmCount));
289 rc = VERR_INVALID_PARAMETER;
290 }
291
292 if (RT_SUCCESS(rc))
293 {
294 for (uint32_t i = 0; i < mParmCount; i++)
295 {
296 if (paDstParms[i].type != mpParms[i].type)
297 {
298 LogFunc(("Parameter %RU32 type mismatch (got %RU32, expected %RU32)\n", i, paDstParms[i].type, mpParms[i].type));
299 rc = VERR_INVALID_PARAMETER;
300 }
301 else
302 {
303 switch (mpParms[i].type)
304 {
305 case VBOX_HGCM_SVC_PARM_32BIT:
306#ifdef DEBUG_andy
307 LogFlowFunc(("\tmpParms[%RU32] = %RU32 (uint32_t)\n",
308 i, mpParms[i].u.uint32));
309#endif
310 paDstParms[i].u.uint32 = mpParms[i].u.uint32;
311 break;
312
313 case VBOX_HGCM_SVC_PARM_64BIT:
314#ifdef DEBUG_andy
315 LogFlowFunc(("\tmpParms[%RU32] = %RU64 (uint64_t)\n",
316 i, mpParms[i].u.uint64));
317#endif
318 paDstParms[i].u.uint64 = mpParms[i].u.uint64;
319 break;
320
321 case VBOX_HGCM_SVC_PARM_PTR:
322 {
323#ifdef DEBUG_andy
324 LogFlowFunc(("\tmpParms[%RU32] = %p (ptr), size = %RU32\n",
325 i, mpParms[i].u.pointer.addr, mpParms[i].u.pointer.size));
326#endif
327 if (!mpParms[i].u.pointer.size)
328 continue; /* Only copy buffer if there actually is something to copy. */
329
330 if (!paDstParms[i].u.pointer.addr)
331 rc = VERR_INVALID_PARAMETER;
332 else if (paDstParms[i].u.pointer.size < mpParms[i].u.pointer.size)
333 rc = VERR_BUFFER_OVERFLOW;
334 else
335 memcpy(paDstParms[i].u.pointer.addr,
336 mpParms[i].u.pointer.addr,
337 mpParms[i].u.pointer.size);
338 break;
339 }
340
341 default:
342 LogFunc(("Parameter %RU32 of type %RU32 is not supported yet\n", i, mpParms[i].type));
343 rc = VERR_NOT_SUPPORTED;
344 break;
345 }
346 }
347
348 if (RT_FAILURE(rc))
349 {
350 LogFunc(("Parameter %RU32 invalid (%Rrc), refusing\n", i, rc));
351 break;
352 }
353 }
354 }
355
356 LogFlowFunc(("Returned with rc=%Rrc\n", rc));
357 return rc;
358 }
359
360 int Assign(const ClientConnection *pConnection)
361 {
362 AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
363
364 int rc;
365
366 LogFlowThisFunc(("[Cmd %RU32] mParmCount=%RU32, mpParms=%p\n", mMsgType, mParmCount, mpParms));
367
368 /* Does the current host command need more parameter space which
369 * the client does not provide yet? */
370 if (mParmCount > pConnection->mNumParms)
371 {
372 LogFlowThisFunc(("[Cmd %RU32] Requires %RU32 parms, only got %RU32 from client\n",
373 mMsgType, mParmCount, pConnection->mNumParms));
374 /*
375 * So this call apparently failed because the guest wanted to peek
376 * how much parameters it has to supply in order to successfully retrieve
377 * this command. Let's tell him so!
378 */
379 rc = VERR_TOO_MUCH_DATA;
380 }
381 else
382 {
383 rc = CopyTo(pConnection->mParms, pConnection->mNumParms);
384
385 /*
386 * Has there been enough parameter space but the wrong parameter types
387 * were submitted -- maybe the client was just asking for the next upcoming
388 * host message?
389 *
390 * Note: To keep this compatible to older clients we return VERR_TOO_MUCH_DATA
391 * in every case.
392 */
393 if (RT_FAILURE(rc))
394 rc = VERR_TOO_MUCH_DATA;
395 }
396
397 return rc;
398 }
399
400 int Peek(const ClientConnection *pConnection)
401 {
402 AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
403
404 LogFlowThisFunc(("[Cmd %RU32] mParmCount=%RU32, mpParms=%p\n", mMsgType, mParmCount, mpParms));
405
406 if (pConnection->mNumParms >= 2)
407 {
408 HGCMSvcSetU32(&pConnection->mParms[0], mMsgType); /* Message ID */
409 HGCMSvcSetU32(&pConnection->mParms[1], mParmCount); /* Required parameters for message */
410 }
411 else
412 LogFlowThisFunc(("Warning: Client has not (yet) submitted enough parameters (%RU32, must be at least 2) to at least peak for the next message\n",
413 pConnection->mNumParms));
414
415 /*
416 * Always return VERR_TOO_MUCH_DATA data here to
417 * keep it compatible with older clients and to
418 * have correct accounting (mHostRc + mHostCmdTries).
419 */
420 return VERR_TOO_MUCH_DATA;
421 }
422} HostCommand;
423typedef std::list< HostCommand *> HostCmdList;
424typedef std::list< HostCommand *>::iterator HostCmdListIter;
425typedef std::list< HostCommand *>::const_iterator HostCmdListIterConst;
426
427/**
428 * Per-client structure used for book keeping/state tracking a
429 * certain host command.
430 */
431typedef struct ClientContext
432{
433 /* Pointer to list node of this command. */
434 HostCommand *mpHostCmd;
435 /** The standard constructor. */
436 ClientContext(void) : mpHostCmd(NULL) {}
437 /** Internal constrcutor. */
438 ClientContext(HostCommand *pHostCmd) : mpHostCmd(pHostCmd) {}
439} ClientContext;
440typedef std::map< uint32_t, ClientContext > ClientContextMap;
441typedef std::map< uint32_t, ClientContext >::iterator ClientContextMapIter;
442typedef std::map< uint32_t, ClientContext >::const_iterator ClientContextMapIterConst;
443
444/**
445 * Structure for holding a connected guest client state.
446 */
447typedef struct ClientState
448{
449 PVBOXHGCMSVCHELPERS mSvcHelpers;
450 /** The client's ID. */
451 uint32_t mID;
452 /** Host command list to process. */
453 HostCmdList mHostCmdList;
454 /** Pending client call (GUEST_MSG_PEEK_WAIT or GUEST_MSG_WAIT), zero if none pending.
455 *
456 * This means the client waits for a new host command to reply and won't return
457 * from the waiting call until a new host command is available. */
458 guestControl::eGuestFn mIsPending;
459 /** The client's pending connection. */
460 ClientConnection mPendingCon;
461 /** Set if we've got a pending wait cancel. */
462 bool m_fPendingCancel;
463 /** Set if master. */
464 bool m_fIsMaster;
465 /** The session ID for this client, UINT32_MAX if not set or master. */
466 uint32_t m_idSession;
467
468
469 ClientState(void)
470 : mSvcHelpers(NULL)
471 , mID(0)
472 , mIsPending((guestControl::eGuestFn)0)
473 , m_fPendingCancel(false)
474 , m_fIsMaster(false)
475 , m_idSession(UINT32_MAX)
476 , mHostCmdRc(VINF_SUCCESS)
477 , mHostCmdTries(0)
478 , mPeekCount(0)
479 { }
480
481 ClientState(PVBOXHGCMSVCHELPERS pSvcHelpers, uint32_t idClient)
482 : mSvcHelpers(pSvcHelpers)
483 , mID(idClient)
484 , mIsPending((guestControl::eGuestFn)0)
485 , m_fPendingCancel(false)
486 , m_fIsMaster(false)
487 , m_idSession(UINT32_MAX)
488 , mHostCmdRc(VINF_SUCCESS)
489 , mHostCmdTries(0)
490 , mPeekCount(0)
491 { }
492
493 /**
494 * Used by for Service::hostProcessCommand().
495 */
496 int EnqueueCommand(HostCommand *pHostCmd)
497 {
498 AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER);
499
500 try
501 {
502 mHostCmdList.push_back(pHostCmd);
503 }
504 catch (std::bad_alloc &)
505 {
506 return VERR_NO_MEMORY;
507 }
508
509 pHostCmd->Retain();
510 return VINF_SUCCESS;
511 }
512
513 /**
514 * Used by for Service::hostProcessCommand().
515 *
516 * @note This wakes up both GUEST_MSG_WAIT and GUEST_MSG_PEEK_WAIT sleepers.
517 */
518 int Wakeup(void)
519 {
520 int rc = VINF_NO_CHANGE;
521
522 if (mIsPending != 0)
523 {
524 LogFlowFunc(("[Client %RU32] Waking up ...\n", mID));
525
526 rc = VINF_SUCCESS;
527
528 HostCmdListIter curCmd = mHostCmdList.begin();
529 if (curCmd != mHostCmdList.end())
530 {
531 HostCommand *pHostCmd = (*curCmd);
532 AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER);
533
534 LogFlowThisFunc(("[Client %RU32] Current host command is %RU32 (CID=%RU32, cParms=%RU32, m_cRefs=%RU32)\n",
535 mID, pHostCmd->mMsgType, pHostCmd->m_idContext, pHostCmd->mParmCount, pHostCmd->m_cRefs));
536
537 if (mIsPending == GUEST_MSG_PEEK_WAIT)
538 {
539 HGCMSvcSetU32(&mPendingCon.mParms[0], pHostCmd->mMsgType);
540 HGCMSvcSetU32(&mPendingCon.mParms[1], pHostCmd->mParmCount);
541 for (uint32_t i = pHostCmd->mParmCount; i >= 2; i--)
542 switch (pHostCmd->mpParms[i - 2].type)
543 {
544 case VBOX_HGCM_SVC_PARM_32BIT: mPendingCon.mParms[i].u.uint32 = ~(uint32_t)sizeof(uint32_t); break;
545 case VBOX_HGCM_SVC_PARM_64BIT: mPendingCon.mParms[i].u.uint32 = ~(uint32_t)sizeof(uint64_t); break;
546 case VBOX_HGCM_SVC_PARM_PTR: mPendingCon.mParms[i].u.uint32 = pHostCmd->mpParms[i - 2].u.pointer.size; break;
547 }
548
549 rc = mSvcHelpers->pfnCallComplete(mPendingCon.mHandle, VINF_SUCCESS);
550 mIsPending = (guestControl::eGuestFn)0;
551 }
552 else if (mIsPending == GUEST_MSG_WAIT)
553 rc = OldRun(&mPendingCon, pHostCmd);
554 else
555 AssertMsgFailed(("mIsPending=%d\n", mIsPending));
556 }
557 else
558 AssertMsgFailed(("Waking up client ID=%RU32 with no host command in queue is a bad idea\n", mID));
559
560 return rc;
561 }
562
563 return VINF_NO_CHANGE;
564 }
565
566 /**
567 * Used by Service::call() to handle GUEST_MSG_CANCEL.
568 *
569 * @note This cancels both GUEST_MSG_WAIT and GUEST_MSG_PEEK_WAIT sleepers.
570 */
571 int CancelWaiting()
572 {
573 LogFlowFunc(("[Client %RU32] Cancelling waiting thread, isPending=%d, pendingNumParms=%RU32, m_idSession=%x\n",
574 mID, mIsPending, mPendingCon.mNumParms, m_idSession));
575
576 /*
577 * The PEEK call is simple: At least two parameters, all set to zero before sleeping.
578 */
579 int rcComplete;
580 if (mIsPending == GUEST_MSG_PEEK_WAIT)
581 {
582 HGCMSvcSetU32(&mPendingCon.mParms[0], HOST_CANCEL_PENDING_WAITS);
583 rcComplete = VINF_TRY_AGAIN;
584 }
585 /*
586 * The GUEST_MSG_WAIT call is complicated, though we're generally here
587 * to wake up someone who is peeking and have two parameters. If there
588 * aren't two parameters, fail the call.
589 */
590 else if (mIsPending != 0)
591 {
592 Assert(mIsPending == GUEST_MSG_WAIT);
593 if (mPendingCon.mNumParms > 0)
594 HGCMSvcSetU32(&mPendingCon.mParms[0], HOST_CANCEL_PENDING_WAITS);
595 if (mPendingCon.mNumParms > 1)
596 HGCMSvcSetU32(&mPendingCon.mParms[1], 0);
597 rcComplete = mPendingCon.mNumParms == 2 ? VINF_SUCCESS : VERR_TRY_AGAIN;
598 }
599 /*
600 * If nobody is waiting, flag the next wait call as cancelled.
601 */
602 else
603 {
604 m_fPendingCancel = true;
605 return VINF_SUCCESS;
606 }
607
608 mSvcHelpers->pfnCallComplete(mPendingCon.mHandle, rcComplete);
609 mIsPending = (guestControl::eGuestFn)0;
610 m_fPendingCancel = false;
611 return VINF_SUCCESS;
612 }
613
614
615 /** @name The GUEST_MSG_WAIT state and helpers.
616 *
617 * @note Don't try understand this, it is certificable!
618 *
619 * @{
620 */
621
622 /** Last (most recent) rc after handling the host command. */
623 int mHostCmdRc;
624 /** How many GUEST_MSG_WAIT calls the client has issued to retrieve one command.
625 *
626 * This is used as a heuristic to remove a message that the client appears not
627 * to be able to successfully retrieve. */
628 uint32_t mHostCmdTries;
629 /** Number of times we've peeked at a pending message.
630 *
631 * This is necessary for being compatible with older Guest Additions. In case
632 * there are commands which only have two (2) parameters and therefore would fit
633 * into the GUEST_MSG_WAIT reply immediately, we now can make sure that the
634 * client first gets back the GUEST_MSG_WAIT results first.
635 */
636 uint32_t mPeekCount;
637
638 /**
639 * Ditches the first host command and crazy GUEST_MSG_WAIT state.
640 *
641 * @note Only used by GUEST_MSG_WAIT scenarios.
642 */
643 void OldDitchFirstHostCmd()
644 {
645 Assert(!mHostCmdList.empty());
646 HostCommand *pHostCmd = *mHostCmdList.begin();
647 AssertPtr(pHostCmd);
648 pHostCmd->SaneRelease();
649 mHostCmdList.pop_front();
650
651 /* Reset state else. */
652 mHostCmdRc = VINF_SUCCESS;
653 mHostCmdTries = 0;
654 mPeekCount = 0;
655 }
656
657 /**
658 * Used by Wakeup() and OldRunCurrent().
659 *
660 * @note Only used by GUEST_MSG_WAIT scenarios.
661 */
662 int OldRun(ClientConnection const *pConnection, HostCommand *pHostCmd)
663 {
664 AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
665 AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER);
666 Assert(*mHostCmdList.begin() == pHostCmd);
667
668 LogFlowFunc(("[Client %RU32] pConnection=%p, mHostCmdRc=%Rrc, mHostCmdTries=%RU32, mPeekCount=%RU32\n",
669 mID, pConnection, mHostCmdRc, mHostCmdTries, mPeekCount));
670
671 int rc = mHostCmdRc = OldSendReply(pConnection, pHostCmd);
672
673 LogFlowThisFunc(("[Client %RU32] Processing command %RU32 ended with rc=%Rrc\n", mID, pHostCmd->mMsgType, mHostCmdRc));
674
675 bool fRemove = false;
676 if (RT_FAILURE(rc))
677 {
678 mHostCmdTries++;
679
680 /*
681 * If the client understood the message but supplied too little buffer space
682 * don't send this message again and drop it after 6 unsuccessful attempts.
683 *
684 * Note: Due to legacy reasons this the retry counter has to be even because on
685 * every peek there will be the actual command retrieval from the client side.
686 * To not get the actual command if the client actually only wants to peek for
687 * the next command, there needs to be two rounds per try, e.g. 3 rounds = 6 tries.
688 */
689 /** @todo Fix the mess stated above. GUEST_MSG_WAIT should be become GUEST_MSG_PEEK, *only*
690 * (and every time) returning the next upcoming host command (if any, blocking). Then
691 * it's up to the client what to do next, either peeking again or getting the actual
692 * host command via an own GUEST_ type message.
693 */
694 if ( rc == VERR_TOO_MUCH_DATA
695 || rc == VERR_CANCELLED)
696 {
697 if (mHostCmdTries == 6)
698 fRemove = true;
699 }
700 /* Client did not understand the message or something else weird happened. Try again one
701 * more time and drop it if it didn't get handled then. */
702 else if (mHostCmdTries > 1)
703 fRemove = true;
704 }
705 else
706 fRemove = true; /* Everything went fine, remove it. */
707
708 LogFlowThisFunc(("[Client %RU32] Tried command %RU32 for %RU32 times, (last result=%Rrc, fRemove=%RTbool)\n",
709 mID, pHostCmd->mMsgType, mHostCmdTries, rc, fRemove));
710
711 if (fRemove)
712 {
713 Assert(*mHostCmdList.begin() == pHostCmd);
714 OldDitchFirstHostCmd();
715 }
716
717 LogFlowFunc(("[Client %RU32] Returned with rc=%Rrc\n", mID, rc));
718 return rc;
719 }
720
721 /**
722 * Set to indicate that a client call (GUEST_MSG_WAIT) is pending.
723 *
724 * @note Only used by GUEST_MSG_WAIT scenarios.
725 */
726 int OldSetPending(const ClientConnection *pConnection)
727 {
728 AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
729
730 if (mIsPending != 0)
731 {
732 LogFlowFunc(("[Client %RU32] Already is in pending mode\n", mID));
733
734 /*
735 * Signal that we don't and can't return yet.
736 */
737 return VINF_HGCM_ASYNC_EXECUTE;
738 }
739
740 if (mHostCmdList.empty())
741 {
742 AssertMsg(mIsPending == 0, ("Client ID=%RU32 already is pending but tried to receive a new host command\n", mID));
743
744 mPendingCon.mHandle = pConnection->mHandle;
745 mPendingCon.mNumParms = pConnection->mNumParms;
746 mPendingCon.mParms = pConnection->mParms;
747
748 mIsPending = GUEST_MSG_WAIT;
749
750 LogFlowFunc(("[Client %RU32] Is now in pending mode\n", mID));
751
752 /*
753 * Signal that we don't and can't return yet.
754 */
755 return VINF_HGCM_ASYNC_EXECUTE;
756 }
757
758 /*
759 * Signal that there already is a connection pending.
760 * Shouldn't happen in daily usage.
761 */
762 AssertMsgFailed(("Client already has a connection pending\n"));
763 return VERR_SIGNAL_PENDING;
764 }
765
766 /**
767 * @note Only used by GUEST_MSG_WAIT scenarios.
768 */
769 int OldRunCurrent(const ClientConnection *pConnection)
770 {
771 AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
772
773 /*
774 * If the host command list is empty, the request must wait for one to be posted.
775 */
776 if (mHostCmdList.empty())
777 {
778 if (!m_fPendingCancel)
779 {
780 /* Go to sleep. */
781 ASSERT_GUEST_RETURN(mIsPending == 0, VERR_WRONG_ORDER);
782 mPendingCon = *pConnection;
783 mIsPending = GUEST_MSG_WAIT;
784 LogFlowFunc(("[Client %RU32] Is now in pending mode\n", mID));
785 return VINF_HGCM_ASYNC_EXECUTE;
786 }
787
788 /* Wait was cancelled. */
789 m_fPendingCancel = false;
790 if (pConnection->mNumParms > 0)
791 HGCMSvcSetU32(&pConnection->mParms[0], HOST_CANCEL_PENDING_WAITS);
792 if (pConnection->mNumParms > 1)
793 HGCMSvcSetU32(&pConnection->mParms[1], 0);
794 return pConnection->mNumParms == 2 ? VINF_SUCCESS : VERR_TRY_AGAIN;
795 }
796
797 /*
798 * Return first host command.
799 */
800 AssertMsgReturn(mIsPending == 0,
801 ("Client ID=%RU32 still is in pending mode; can't use another connection\n", mID), VERR_INVALID_PARAMETER);
802
803 HostCmdListIter curCmd = mHostCmdList.begin();
804 Assert(curCmd != mHostCmdList.end());
805 HostCommand *pHostCmd = *curCmd;
806 AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER);
807
808 return OldRun(pConnection, pHostCmd);
809 }
810
811 /**
812 * Internal worker for OldRun().
813 * @note Only used for GUEST_MSG_WAIT.
814 */
815 int OldSendReply(ClientConnection const *pConnection,
816 HostCommand *pHostCmd)
817 {
818 AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
819 AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER);
820
821 /* In case of VERR_CANCELLED. */
822 uint32_t const cSavedPeeks = mPeekCount;
823
824 int rc;
825 /* If the client is in pending mode, always send back
826 * the peek result first. */
827 if (mIsPending)
828 {
829 Assert(mIsPending == GUEST_MSG_WAIT);
830 rc = pHostCmd->Peek(pConnection);
831 mPeekCount++;
832 }
833 else
834 {
835 /* If this is the very first peek, make sure to *always* give back the peeking answer
836 * instead of the actual command, even if this command would fit into the current
837 * connection buffer. */
838 if (!mPeekCount)
839 {
840 rc = pHostCmd->Peek(pConnection);
841 mPeekCount++;
842 }
843 else
844 {
845 /* Try assigning the host command to the client and store the
846 * result code for later use. */
847 rc = pHostCmd->Assign(pConnection);
848 if (RT_FAILURE(rc)) /* If something failed, let the client peek (again). */
849 {
850 rc = pHostCmd->Peek(pConnection);
851 mPeekCount++;
852 }
853 else
854 mPeekCount = 0;
855 }
856 }
857
858 /* Reset pending status. */
859 mIsPending = (guestControl::eGuestFn)0;
860
861 /* In any case the client did something, so complete
862 * the pending call with the result we just got. */
863 AssertPtr(mSvcHelpers);
864 int rc2 = mSvcHelpers->pfnCallComplete(pConnection->mHandle, rc);
865
866 /* Rollback in case the guest cancelled the call. */
867 if (rc2 == VERR_CANCELLED && RT_SUCCESS(rc))
868 {
869 mPeekCount = cSavedPeeks;
870 rc = VERR_CANCELLED;
871 }
872
873 LogFlowThisFunc(("[Client %RU32] Command %RU32 ended with %Rrc (mPeekCount=%RU32, pConnection=%p)\n",
874 mID, pHostCmd->mMsgType, rc, mPeekCount, pConnection));
875 return rc;
876 }
877
878 /** @} */
879} ClientState;
880typedef std::map< uint32_t, ClientState > ClientStateMap;
881typedef std::map< uint32_t, ClientState >::iterator ClientStateMapIter;
882typedef std::map< uint32_t, ClientState >::const_iterator ClientStateMapIterConst;
883
884/**
885 * Prepared session (GUEST_SESSION_PREPARE).
886 */
887typedef struct GstCtrlPreparedSession
888{
889 /** List entry. */
890 RTLISTNODE ListEntry;
891 /** The session ID. */
892 uint32_t idSession;
893 /** The key size. */
894 uint32_t cbKey;
895 /** The key bytes. */
896 uint8_t abKey[RT_FLEXIBLE_ARRAY];
897} GstCtrlPreparedSession;
898
899
900/**
901 * Class containing the shared information service functionality.
902 */
903class GstCtrlService : public RTCNonCopyable
904{
905
906private:
907
908 /** Type definition for use in callback functions. */
909 typedef GstCtrlService SELF;
910 /** HGCM helper functions. */
911 PVBOXHGCMSVCHELPERS mpHelpers;
912 /** Callback function supplied by the host for notification of updates to properties. */
913 PFNHGCMSVCEXT mpfnHostCallback;
914 /** User data pointer to be supplied to the host callback function. */
915 void *mpvHostData;
916 /** List containing all buffered host commands. */
917 RTLISTANCHOR mHostCmdList;
918 /** Map containing all connected clients, key is HGCM client ID. */
919 ClientStateMap mClientStateMap;
920 /** The master HGCM client ID, UINT32_MAX if none. */
921 uint32_t m_idMasterClient;
922 /** Set if we're in legacy mode (pre 6.0). */
923 bool m_fLegacyMode;
924 /** Number of prepared sessions. */
925 uint32_t m_cPreparedSessions;
926 /** List of prepared session (GstCtrlPreparedSession). */
927 RTLISTANCHOR m_PreparedSessions;
928
929public:
930 explicit GstCtrlService(PVBOXHGCMSVCHELPERS pHelpers)
931 : mpHelpers(pHelpers)
932 , mpfnHostCallback(NULL)
933 , mpvHostData(NULL)
934 , m_idMasterClient(UINT32_MAX)
935 , m_fLegacyMode(true)
936 , m_cPreparedSessions(0)
937 {
938 RTListInit(&mHostCmdList);
939 RTListInit(&m_PreparedSessions);
940 }
941
942 static DECLCALLBACK(int) svcUnload(void *pvService);
943 static DECLCALLBACK(int) svcConnect(void *pvService, uint32_t u32ClientID, void *pvClient,
944 uint32_t fRequestor, bool fRestoring);
945 static DECLCALLBACK(int) svcDisconnect(void *pvService, uint32_t u32ClientID, void *pvClient);
946 static DECLCALLBACK(void) svcCall(void *pvService, VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient,
947 uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[], uint64_t tsArrival);
948 static DECLCALLBACK(int) svcHostCall(void *pvService, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
949 static DECLCALLBACK(int) svcRegisterExtension(void *pvService, PFNHGCMSVCEXT pfnExtension, void *pvExtension);
950
951private:
952 int clientMakeMeMaster(uint32_t idClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms);
953 int clientMsgPeek(uint32_t idClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool fWait);
954 int clientMsgGet(uint32_t idClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
955 int clientMsgCancel(uint32_t idClient, uint32_t cParms);
956 int clientMsgSkip(uint32_t idClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms);
957 int clientSessionPrepare(uint32_t idClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
958 int clientSessionCancelPrepared(uint32_t idClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
959 int clientSessionAccept(uint32_t idClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
960 int clientSessionCloseOther(uint32_t idClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
961
962 int clientMsgOldGet(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
963 int clientMsgOldFilterSet(uint32_t u32ClientID, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
964 int clientMsgOldSkip(uint32_t u32ClientID, uint32_t cParms);
965
966 int hostCallback(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
967 int hostProcessCommand(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
968
969 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(GstCtrlService);
970};
971
972
973/**
974 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnUnload,
975 * Simply deletes the GstCtrlService object}
976 */
977/*static*/ DECLCALLBACK(int)
978GstCtrlService::svcUnload(void *pvService)
979{
980 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
981 SELF *pThis = reinterpret_cast<SELF *>(pvService);
982 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
983
984 delete pThis;
985
986 return VINF_SUCCESS;
987}
988
989
990
991/**
992 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnConnect,
993 * Initializes the state for a new client.}
994 */
995/*static*/ DECLCALLBACK(int)
996GstCtrlService::svcConnect(void *pvService, uint32_t idClient, void *pvClient, uint32_t fRequestor, bool fRestoring)
997{
998 LogFlowFunc(("[Client %RU32] Connected\n", idClient));
999
1000 RT_NOREF(fRestoring, pvClient);
1001 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
1002 SELF *pThis = reinterpret_cast<SELF *>(pvService);
1003 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1004
1005 AssertMsg(pThis->mClientStateMap.find(idClient) == pThis->mClientStateMap.end(),
1006 ("Client with ID=%RU32 already connected when it should not\n", idClient));
1007
1008 /*
1009 * Create client state.
1010 */
1011 try
1012 {
1013 pThis->mClientStateMap[idClient] = ClientState(pThis->mpHelpers, idClient);
1014 }
1015 catch (std::bad_alloc)
1016 {
1017 return VERR_NO_MEMORY;
1018 }
1019 ClientState &rClientState = pThis->mClientStateMap[idClient];
1020
1021 /*
1022 * For legacy compatibility reasons we have to pick a master client at some
1023 * point, so if the /dev/vboxguest requirements checks out we pick the first
1024 * one through the door.
1025 */
1026/** @todo make picking the master more dynamic/flexible. */
1027 if ( pThis->m_fLegacyMode
1028 && pThis->m_idMasterClient == UINT32_MAX)
1029 {
1030 if ( fRequestor == VMMDEV_REQUESTOR_LEGACY
1031 || !(fRequestor & VMMDEV_REQUESTOR_USER_DEVICE))
1032 {
1033 LogFunc(("Picking %u as master for now.\n", idClient));
1034 pThis->m_idMasterClient = idClient;
1035 rClientState.m_fIsMaster = true;
1036 }
1037 }
1038
1039 return VINF_SUCCESS;
1040}
1041
1042
1043/**
1044 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnConnect,
1045 * Handles a client which disconnected.}
1046 *
1047 * This functiond does some internal cleanup as well as sends notifications to
1048 * the host so that the host can do the same (if required).
1049 */
1050/*static*/ DECLCALLBACK(int)
1051GstCtrlService::svcDisconnect(void *pvService, uint32_t idClient, void *pvClient)
1052{
1053 RT_NOREF(pvClient);
1054 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
1055 SELF *pThis = reinterpret_cast<SELF *>(pvService);
1056 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1057 LogFlowFunc(("[Client %RU32] Disconnected (%zu clients total)\n", idClient, pThis->mClientStateMap.size()));
1058
1059 ClientStateMapIter ItClientState = pThis->mClientStateMap.find(idClient);
1060 AssertMsgReturn(ItClientState != pThis->mClientStateMap.end(),
1061 ("Client ID=%RU32 not found in client list when it should be there\n", idClient),
1062 VINF_SUCCESS);
1063
1064 /*
1065 * Cancel all pending host commands, replying with GUEST_DISCONNECTED if final recipient.
1066 */
1067 {
1068 ClientState &rClientState = ItClientState->second;
1069
1070 while (!rClientState.mHostCmdList.empty())
1071 {
1072 HostCommand *pHostCmd = *rClientState.mHostCmdList.begin();
1073 rClientState.mHostCmdList.pop_front();
1074
1075 uint32_t idFunction = pHostCmd->mMsgType;
1076 uint32_t idContext = pHostCmd->m_idContext;
1077 if (pHostCmd->SaneRelease() == 0)
1078 {
1079 VBOXHGCMSVCPARM Parm;
1080 HGCMSvcSetU32(&Parm, idContext);
1081 int rc2 = pThis->hostCallback(GUEST_DISCONNECTED, 1, &Parm);
1082 LogFlowFunc(("Cancelled host command %u (%s) with idContext=%#x -> %Rrc\n",
1083 idFunction, GstCtrlHostFnName((eHostFn)idFunction), idContext, rc2));
1084 RT_NOREF(rc2, idFunction);
1085 }
1086 }
1087 }
1088
1089 /*
1090 * Delete the client state.
1091 */
1092 pThis->mClientStateMap.erase(ItClientState);
1093
1094 /*
1095 * If it's the master disconnecting, we need to reset related globals.
1096 */
1097 if (idClient == pThis->m_idMasterClient)
1098 {
1099 pThis->m_idMasterClient = UINT32_MAX;
1100 GstCtrlPreparedSession *pCur, *pNext;
1101 RTListForEachSafe(&pThis->m_PreparedSessions, pCur, pNext, GstCtrlPreparedSession, ListEntry)
1102 {
1103 RTMemFree(pCur);
1104 }
1105 pThis->m_cPreparedSessions = 0;
1106 }
1107
1108 if (pThis->mClientStateMap.empty())
1109 pThis->m_fLegacyMode = true;
1110
1111 /*
1112 * Host command sanity check.
1113 */
1114 Assert(RTListIsEmpty(&pThis->mHostCmdList) || !pThis->mClientStateMap.empty());
1115
1116 return VINF_SUCCESS;
1117}
1118
1119
1120/**
1121 * A client asks for the next message to process.
1122 *
1123 * This either fills in a pending host command into the client's parameter space
1124 * or defers the guest call until we have something from the host.
1125 *
1126 * @return IPRT status code.
1127 * @param u32ClientID The client's ID.
1128 * @param callHandle The client's call handle.
1129 * @param cParms Number of parameters.
1130 * @param paParms Array of parameters.
1131 */
1132int GstCtrlService::clientMsgOldGet(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle,
1133 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1134{
1135 /*
1136 * Lookup client in our map so that we can assign the context ID of
1137 * a command to that client.
1138 */
1139 ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID);
1140 AssertMsg(itClientState != mClientStateMap.end(), ("Client with ID=%RU32 not found when it should be present\n",
1141 u32ClientID));
1142 if (itClientState == mClientStateMap.end())
1143 {
1144 /* Should never happen. Complete the call on the guest side though. */
1145 AssertPtr(mpHelpers);
1146 mpHelpers->pfnCallComplete(callHandle, VERR_NOT_FOUND);
1147
1148 return VERR_NOT_FOUND;
1149 }
1150
1151 ClientState &clientState = itClientState->second;
1152
1153 /* Use the current (inbound) connection. */
1154 ClientConnection thisCon;
1155 thisCon.mHandle = callHandle;
1156 thisCon.mNumParms = cParms;
1157 thisCon.mParms = paParms;
1158
1159 return clientState.OldRunCurrent(&thisCon);
1160}
1161
1162
1163/**
1164 * Implements GUEST_MAKE_ME_MASTER.
1165 *
1166 * @returns VBox status code.
1167 * @retval VINF_HGCM_ASYNC_EXECUTE on success (we complete the message here).
1168 * @retval VERR_ACCESS_DENIED if not using main VBoxGuest device not
1169 * @retval VERR_RESOURCE_BUSY if there is already a master.
1170 * @retval VERR_VERSION_MISMATCH if VBoxGuest didn't supply requestor info.
1171 * @retval VERR_INVALID_CLIENT_ID
1172 * @retval VERR_WRONG_PARAMETER_COUNT
1173 *
1174 * @param idClient The client's ID.
1175 * @param hCall The client's call handle.
1176 * @param cParms Number of parameters.
1177 */
1178int GstCtrlService::clientMakeMeMaster(uint32_t idClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms)
1179{
1180 /*
1181 * Validate the request.
1182 */
1183 ASSERT_GUEST_RETURN(cParms == 0, VERR_WRONG_PARAMETER_COUNT);
1184
1185 uint32_t fRequestor = mpHelpers->pfnGetRequestor(hCall);
1186 ASSERT_GUEST_LOGREL_MSG_RETURN(fRequestor != VMMDEV_REQUESTOR_LEGACY,
1187 ("Outdated VBoxGuest w/o requestor support. Please update!\n"),
1188 VERR_VERSION_MISMATCH);
1189 ASSERT_GUEST_LOGREL_MSG_RETURN(!(fRequestor & VMMDEV_REQUESTOR_USER_DEVICE), ("fRequestor=%#x\n", fRequestor),
1190 VERR_ACCESS_DENIED);
1191
1192 ClientStateMapIter ItClientState = mClientStateMap.find(idClient);
1193 ASSERT_GUEST_MSG_RETURN(ItClientState != mClientStateMap.end(), ("idClient=%RU32\n", idClient), VERR_INVALID_CLIENT_ID);
1194 ClientState &rClientState = ItClientState->second;
1195
1196 /*
1197 * Do the work.
1198 */
1199 ASSERT_GUEST_MSG_RETURN(m_idMasterClient == idClient || m_idMasterClient == UINT32_MAX,
1200 ("Already have master session %RU32, refusing %RU32.\n", m_idMasterClient, idClient),
1201 VERR_RESOURCE_BUSY);
1202 int rc = mpHelpers->pfnCallComplete(hCall, VINF_SUCCESS);
1203 if (RT_SUCCESS(rc))
1204 {
1205 m_idMasterClient = idClient;
1206 m_fLegacyMode = false;
1207 rClientState.m_fIsMaster = true;
1208 }
1209 else
1210 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
1211
1212 return VINF_HGCM_ASYNC_EXECUTE;
1213}
1214
1215/**
1216 * Implements GUEST_MSG_PEEK_WAIT and GUEST_MSG_PEEK_NOWAIT.
1217 *
1218 * @returns VBox status code.
1219 * @retval VINF_SUCCESS if a message was pending and is being returned.
1220 * @retval VERR_TRY_AGAIN if no message pending and not blocking.
1221 * @retval VERR_RESOURCE_BUSY if another read already made a waiting call.
1222 * @retval VINF_HGCM_ASYNC_EXECUTE if message wait is pending.
1223 *
1224 * @param idClient The client's ID.
1225 * @param hCall The client's call handle.
1226 * @param cParms Number of parameters.
1227 * @param paParms Array of parameters.
1228 * @param fWait Set if we should wait for a message, clear if to return
1229 * immediately.
1230 */
1231int GstCtrlService::clientMsgPeek(uint32_t idClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool fWait)
1232{
1233 /*
1234 * Validate the request.
1235 */
1236 ASSERT_GUEST_MSG_RETURN(cParms >= 2, ("cParms=%u!\n", cParms), VERR_WRONG_PARAMETER_COUNT);
1237 for (uint32_t i = 0; i < cParms; i++)
1238 {
1239 ASSERT_GUEST_MSG_RETURN(paParms[i].type == VBOX_HGCM_SVC_PARM_32BIT, ("#%u type=%u\n", i, paParms[i].type),
1240 VERR_WRONG_PARAMETER_TYPE);
1241 paParms[i].u.uint32 = 0;
1242 }
1243
1244 ClientStateMapIter ItClientState = mClientStateMap.find(idClient);
1245 ASSERT_GUEST_MSG_RETURN(ItClientState != mClientStateMap.end(), ("idClient=%RU32\n", idClient), VERR_INVALID_CLIENT_ID);
1246 ClientState &rClientState = ItClientState->second;
1247
1248 /*
1249 * Return information about the first command if one is pending in the list.
1250 */
1251 HostCmdListIter itFirstCmd = rClientState.mHostCmdList.begin();
1252 if (itFirstCmd != rClientState.mHostCmdList.end())
1253 {
1254 HostCommand *pFirstCmd = *itFirstCmd;
1255 paParms[0].u.uint32 = pFirstCmd->mMsgType;
1256 paParms[1].u.uint32 = pFirstCmd->mParmCount;
1257 for (uint32_t i = pFirstCmd->mParmCount; i >= 2; i--)
1258 switch (pFirstCmd->mpParms[i - 2].type)
1259 {
1260 case VBOX_HGCM_SVC_PARM_32BIT: paParms[i].u.uint32 = ~(uint32_t)sizeof(uint32_t); break;
1261 case VBOX_HGCM_SVC_PARM_64BIT: paParms[i].u.uint32 = ~(uint32_t)sizeof(uint64_t); break;
1262 case VBOX_HGCM_SVC_PARM_PTR: paParms[i].u.uint32 = pFirstCmd->mpParms[i - 2].u.pointer.size; break;
1263 }
1264
1265 LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_XXXX -> VINF_SUCCESS (idMsg=%u (%s), cParms=%u)\n",
1266 idClient, pFirstCmd->mMsgType, GstCtrlHostFnName((eHostFn)pFirstCmd->mMsgType), pFirstCmd->mParmCount));
1267 return VINF_SUCCESS;
1268 }
1269
1270 /*
1271 * If we cannot wait, fail the call.
1272 */
1273 if (!fWait)
1274 {
1275 LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_NOWAIT -> VERR_TRY_AGAIN\n", idClient));
1276 return VERR_TRY_AGAIN;
1277 }
1278
1279 /*
1280 * Wait for the host to queue a message for this client.
1281 */
1282 ASSERT_GUEST_MSG_RETURN(rClientState.mIsPending == 0, ("Already pending! (idClient=%RU32)\n", idClient), VERR_RESOURCE_BUSY);
1283 rClientState.mPendingCon.mHandle = hCall;
1284 rClientState.mPendingCon.mNumParms = cParms;
1285 rClientState.mPendingCon.mParms = paParms;
1286 rClientState.mIsPending = GUEST_MSG_PEEK_WAIT;
1287 LogFlowFunc(("[Client %RU32] Is now in pending mode\n", idClient));
1288 return VINF_HGCM_ASYNC_EXECUTE;
1289}
1290
1291/**
1292 * Implements GUEST_MSG_GET.
1293 *
1294 * @returns VBox status code.
1295 * @retval VINF_SUCCESS if message retrieved and removed from the pending queue.
1296 * @retval VERR_TRY_AGAIN if no message pending.
1297 * @retval VERR_BUFFER_OVERFLOW if a parmeter buffer is too small. The buffer
1298 * size was updated to reflect the required size, though this isn't yet
1299 * forwarded to the guest. (The guest is better of using peek with
1300 * parameter count + 2 parameters to get the sizes.)
1301 * @retval VERR_MISMATCH if the incoming message ID does not match the pending.
1302 * @retval VINF_HGCM_ASYNC_EXECUTE if message was completed already.
1303 *
1304 * @param idClient The client's ID.
1305 * @param hCall The client's call handle.
1306 * @param cParms Number of parameters.
1307 * @param paParms Array of parameters.
1308 */
1309int GstCtrlService::clientMsgGet(uint32_t idClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1310{
1311 /*
1312 * Validate the request.
1313 *
1314 * The weird first parameter logic is due to GUEST_MSG_WAIT compatibility
1315 * (don't want to rewrite all the message structures).
1316 */
1317 uint32_t const idMsgExpected = cParms > 0 && paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT ? paParms[0].u.uint32
1318 : cParms > 0 && paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT ? paParms[0].u.uint64
1319 : UINT32_MAX;
1320
1321 ClientStateMapIter ItClientState = mClientStateMap.find(idClient);
1322 ASSERT_GUEST_MSG_RETURN(ItClientState != mClientStateMap.end(), ("idClient=%RU32\n", idClient), VERR_INVALID_CLIENT_ID);
1323
1324 ClientState &rClientState = ItClientState->second;
1325
1326 /*
1327 * Return information aobut the first command if one is pending in the list.
1328 */
1329 HostCmdListIter itFirstCmd = rClientState.mHostCmdList.begin();
1330 if (itFirstCmd != rClientState.mHostCmdList.end())
1331 {
1332 HostCommand *pFirstCmd = *itFirstCmd;
1333
1334 ASSERT_GUEST_MSG_RETURN(pFirstCmd->mMsgType == idMsgExpected || idMsgExpected == UINT32_MAX,
1335 ("idMsg=%u (%s) cParms=%u, caller expected %u (%s) and %u\n",
1336 pFirstCmd->mMsgType, GstCtrlHostFnName((eHostFn)pFirstCmd->mMsgType), pFirstCmd->mParmCount,
1337 idMsgExpected, GstCtrlHostFnName((eHostFn)idMsgExpected), cParms),
1338 VERR_MISMATCH);
1339 ASSERT_GUEST_MSG_RETURN(pFirstCmd->mParmCount == cParms,
1340 ("idMsg=%u (%s) cParms=%u, caller expected %u (%s) and %u\n",
1341 pFirstCmd->mMsgType, GstCtrlHostFnName((eHostFn)pFirstCmd->mMsgType), pFirstCmd->mParmCount,
1342 idMsgExpected, GstCtrlHostFnName((eHostFn)idMsgExpected), cParms),
1343 VERR_WRONG_PARAMETER_COUNT);
1344
1345 /* Check the parameter types. */
1346 for (uint32_t i = 0; i < cParms; i++)
1347 ASSERT_GUEST_MSG_RETURN(pFirstCmd->mpParms[i].type == paParms[i].type,
1348 ("param #%u: type %u, caller expected %u (idMsg=%u %s)\n", i, pFirstCmd->mpParms[i].type,
1349 paParms[i].type, pFirstCmd->mMsgType, GstCtrlHostFnName((eHostFn)pFirstCmd->mMsgType)),
1350 VERR_WRONG_PARAMETER_TYPE);
1351
1352 /*
1353 * Copy out the parameters.
1354 *
1355 * No assertions on buffer overflows, and keep going till the end so we can
1356 * communicate all the required buffer sizes.
1357 */
1358 int rc = VINF_SUCCESS;
1359 for (uint32_t i = 0; i < cParms; i++)
1360 switch (pFirstCmd->mpParms[i].type)
1361 {
1362 case VBOX_HGCM_SVC_PARM_32BIT:
1363 paParms[i].u.uint32 = pFirstCmd->mpParms[i].u.uint32;
1364 break;
1365
1366 case VBOX_HGCM_SVC_PARM_64BIT:
1367 paParms[i].u.uint64 = pFirstCmd->mpParms[i].u.uint64;
1368 break;
1369
1370 case VBOX_HGCM_SVC_PARM_PTR:
1371 {
1372 uint32_t const cbSrc = pFirstCmd->mpParms[i].u.pointer.size;
1373 uint32_t const cbDst = paParms[i].u.pointer.size;
1374 paParms[i].u.pointer.size = cbSrc; /** @todo Check if this is safe in other layers...
1375 * Update: Safe, yes, but VMMDevHGCM doesn't pass it along. */
1376 if (cbSrc <= cbDst)
1377 memcpy(paParms[i].u.pointer.addr, pFirstCmd->mpParms[i].u.pointer.addr, cbSrc);
1378 else
1379 rc = VERR_BUFFER_OVERFLOW;
1380 break;
1381 }
1382
1383 default:
1384 AssertMsgFailed(("#%u: %u\n", i, pFirstCmd->mpParms[i].type));
1385 rc = VERR_INTERNAL_ERROR;
1386 break;
1387 }
1388 if (RT_SUCCESS(rc))
1389 {
1390 /*
1391 * Complete the command and remove the pending message unless the
1392 * guest raced us and cancelled this call in the meantime.
1393 */
1394 AssertPtr(mpHelpers);
1395 rc = mpHelpers->pfnCallComplete(hCall, rc);
1396 if (rc != VERR_CANCELLED)
1397 {
1398 rClientState.mHostCmdList.erase(itFirstCmd);
1399 pFirstCmd->SaneRelease();
1400 }
1401 else
1402 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
1403 return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
1404 }
1405 return rc;
1406 }
1407
1408 paParms[0].u.uint32 = 0;
1409 paParms[1].u.uint32 = 0;
1410 LogFlowFunc(("[Client %RU32] GUEST_MSG_GET -> VERR_TRY_AGAIN\n", idClient));
1411 return VERR_TRY_AGAIN;
1412}
1413
1414/**
1415 * Implements GUEST_MSG_CANCEL.
1416 *
1417 * @returns VBox status code.
1418 * @retval VINF_SUCCESS if cancelled any calls.
1419 * @retval VWRN_NOT_FOUND if no callers.
1420 * @retval VINF_HGCM_ASYNC_EXECUTE if message wait is pending.
1421 *
1422 * @param idClient The client's ID.
1423 * @param cParms Number of parameters.
1424 */
1425int GstCtrlService::clientMsgCancel(uint32_t idClient, uint32_t cParms)
1426{
1427 /*
1428 * Validate the request.
1429 */
1430 ASSERT_GUEST_MSG_RETURN(cParms == 0, ("cParms=%u!\n", cParms), VERR_WRONG_PARAMETER_COUNT);
1431
1432 ClientStateMapIter ItClientState = mClientStateMap.find(idClient);
1433 ASSERT_GUEST_MSG_RETURN(ItClientState != mClientStateMap.end(), ("idClient=%RU32\n", idClient), VERR_INVALID_CLIENT_ID);
1434 ClientState &rClientState = ItClientState->second;
1435
1436 if (rClientState.mIsPending != 0)
1437 {
1438 rClientState.CancelWaiting();
1439 return VINF_SUCCESS;
1440 }
1441 return VWRN_NOT_FOUND;
1442}
1443
1444
1445/**
1446 * Implements GUEST_MSG_SKIP.
1447 *
1448 * @returns VBox status code.
1449 * @retval VINF_HGCM_ASYNC_EXECUTE on success as we complete the message.
1450 * @retval VERR_NOT_FOUND if no message pending.
1451 *
1452 * @param idClient The client's ID.
1453 * @param hCall The call handle for completing it.
1454 * @param cParms Number of parameters.
1455 */
1456int GstCtrlService::clientMsgSkip(uint32_t idClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms)
1457{
1458 /*
1459 * Validate the call.
1460 */
1461 ASSERT_GUEST_RETURN(cParms == 0, VERR_WRONG_PARAMETER_COUNT);
1462
1463 ClientStateMapIter ItClientState = mClientStateMap.find(idClient);
1464 ASSERT_GUEST_MSG_RETURN(ItClientState != mClientStateMap.end(), ("idClient=%RU32\n", idClient), VERR_INVALID_CLIENT_ID);
1465 ClientState &rClientState = ItClientState->second;
1466
1467 /*
1468 * Do the job.
1469 */
1470 if (!rClientState.mHostCmdList.empty())
1471 {
1472 int rc = mpHelpers->pfnCallComplete(hCall, VERR_NOT_FOUND);
1473 if (RT_SUCCESS(rc))
1474 {
1475 /*
1476 * Remove the command from the queue.
1477 */
1478 HostCommand *pFirstCmd = *rClientState.mHostCmdList.begin();
1479 rClientState.mHostCmdList.pop_front();
1480
1481 /*
1482 * Compose a reply to the host service.
1483 */
1484 VBOXHGCMSVCPARM aReplyParams[4];
1485 HGCMSvcSetU32(&aReplyParams[0], pFirstCmd->m_idContext);
1486 HGCMSvcSetU32(&aReplyParams[1], pFirstCmd->mMsgType);
1487 HGCMSvcSetU32(&aReplyParams[2], (uint32_t)VERR_NOT_SUPPORTED);
1488 HGCMSvcSetPv(&aReplyParams[3], NULL, 0);
1489 GstCtrlService::hostCallback(GUEST_MSG_REPLY, RT_ELEMENTS(aReplyParams), aReplyParams);
1490
1491 /*
1492 * Free the command.
1493 */
1494 pFirstCmd->SaneRelease();
1495 }
1496 else
1497 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
1498 return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
1499 }
1500 return VERR_NOT_FOUND;
1501}
1502
1503
1504/**
1505 * Implements GUEST_SESSION_PREPARE.
1506 *
1507 * @returns VBox status code.
1508 * @retval VINF_HGCM_ASYNC_EXECUTE on success as we complete the message.
1509 * @retval VERR_OUT_OF_RESOURCES if too many pending sessions hanging around.
1510 * @retval VERR_OUT_OF_RANGE if the session ID outside the allowed range.
1511 * @retval VERR_BUFFER_OVERFLOW if key too large.
1512 * @retval VERR_BUFFER_UNDERFLOW if key too small.
1513 * @retval VERR_ACCESS_DENIED if not master or in legacy mode.
1514 * @retval VERR_DUPLICATE if the session ID has been prepared already.
1515 *
1516 * @param idClient The client's ID.
1517 * @param hCall The call handle for completing it.
1518 * @param cParms Number of parameters.
1519 * @param paParms The parameters.
1520 */
1521int GstCtrlService::clientSessionPrepare(uint32_t idClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1522{
1523 /*
1524 * Validate parameters.
1525 */
1526 ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
1527 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1528 uint32_t const idSession = paParms[0].u.uint32;
1529 ASSERT_GUEST_RETURN(idSession >= 1, VERR_OUT_OF_RANGE);
1530 ASSERT_GUEST_RETURN(idSession <= 0xfff0, VERR_OUT_OF_RANGE);
1531
1532 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_PTR, VERR_WRONG_PARAMETER_TYPE);
1533 uint32_t const cbKey = paParms[1].u.pointer.size;
1534 void const *pvKey = paParms[1].u.pointer.addr;
1535 ASSERT_GUEST_RETURN(cbKey >= 64, VERR_BUFFER_UNDERFLOW);
1536 ASSERT_GUEST_RETURN(cbKey <= _16K, VERR_BUFFER_OVERFLOW);
1537
1538 ClientStateMapIter ItClientState = mClientStateMap.find(idClient);
1539 ASSERT_GUEST_MSG_RETURN(ItClientState != mClientStateMap.end(), ("idClient=%RU32\n", idClient), VERR_INVALID_CLIENT_ID);
1540 ClientState &rClientState = ItClientState->second;
1541 ASSERT_GUEST_RETURN(rClientState.m_fIsMaster, VERR_ACCESS_DENIED);
1542 ASSERT_GUEST_RETURN(!m_fLegacyMode, VERR_ACCESS_DENIED);
1543 Assert(m_idMasterClient == idClient);
1544
1545 /* Now that we know it's the master, we can check for session ID duplicates. */
1546 GstCtrlPreparedSession *pCur;
1547 RTListForEach(&m_PreparedSessions, pCur, GstCtrlPreparedSession, ListEntry)
1548 {
1549 ASSERT_GUEST_RETURN(pCur->idSession != idSession, VERR_DUPLICATE);
1550 }
1551
1552 /*
1553 * Make a copy of the session ID and key.
1554 */
1555 ASSERT_GUEST_RETURN(m_cPreparedSessions < 128, VERR_OUT_OF_RESOURCES);
1556
1557 GstCtrlPreparedSession *pPrepped = (GstCtrlPreparedSession *)RTMemAlloc(RT_UOFFSETOF_DYN(GstCtrlPreparedSession, abKey[cbKey]));
1558 AssertReturn(pPrepped, VERR_NO_MEMORY);
1559 pPrepped->idSession = idSession;
1560 pPrepped->cbKey = cbKey;
1561 memcpy(pPrepped->abKey, pvKey, cbKey);
1562
1563 RTListAppend(&m_PreparedSessions, &pPrepped->ListEntry);
1564 m_cPreparedSessions++;
1565
1566 /*
1567 * Try complete the command.
1568 */
1569 int rc = mpHelpers->pfnCallComplete(hCall, VERR_NOT_FOUND);
1570 if (RT_SUCCESS(rc))
1571 LogFlow(("Prepared %u with a %#x byte key (%u pending).\n", idSession, cbKey, m_cPreparedSessions));
1572 else
1573 {
1574 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
1575 RTListNodeRemove(&pPrepped->ListEntry);
1576 RTMemFree(pPrepped);
1577 m_cPreparedSessions--;
1578 }
1579 return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
1580}
1581
1582
1583/**
1584 * Implements GUEST_SESSION_CANCEL_PREPARED.
1585 *
1586 * @returns VBox status code.
1587 * @retval VINF_HGCM_ASYNC_EXECUTE on success as we complete the message.
1588 * @retval VWRN_NOT_FOUND if no session with the specified ID.
1589 * @retval VERR_ACCESS_DENIED if not master or in legacy mode.
1590 *
1591 * @param idClient The client's ID.
1592 * @param cParms Number of parameters.
1593 * @param paParms The parameters.
1594 */
1595int GstCtrlService::clientSessionCancelPrepared(uint32_t idClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1596{
1597 /*
1598 * Validate parameters.
1599 */
1600 ASSERT_GUEST_RETURN(cParms == 1, VERR_WRONG_PARAMETER_COUNT);
1601 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1602 uint32_t const idSession = paParms[0].u.uint32;
1603
1604 ClientStateMapIter ItClientState = mClientStateMap.find(idClient);
1605 ASSERT_GUEST_MSG_RETURN(ItClientState != mClientStateMap.end(), ("idClient=%RU32\n", idClient), VERR_INVALID_CLIENT_ID);
1606 ClientState &rClientState = ItClientState->second;
1607 ASSERT_GUEST_RETURN(rClientState.m_fIsMaster, VERR_ACCESS_DENIED);
1608 ASSERT_GUEST_RETURN(!m_fLegacyMode, VERR_ACCESS_DENIED);
1609 Assert(m_idMasterClient == idClient);
1610
1611 /*
1612 * Do the work.
1613 */
1614 int rc = VWRN_NOT_FOUND;
1615 if (idSession == UINT32_MAX)
1616 {
1617 GstCtrlPreparedSession *pCur, *pNext;
1618 RTListForEachSafe(&m_PreparedSessions, pCur, pNext, GstCtrlPreparedSession, ListEntry)
1619 {
1620 RTMemFree(pCur);
1621 rc = VINF_SUCCESS;
1622 }
1623 m_cPreparedSessions = 0;
1624 }
1625 else
1626 {
1627 GstCtrlPreparedSession *pCur, *pNext;
1628 RTListForEachSafe(&m_PreparedSessions, pCur, pNext, GstCtrlPreparedSession, ListEntry)
1629 {
1630 if (pCur->idSession == idSession)
1631 {
1632 RTMemFree(pCur);
1633 m_cPreparedSessions -= 1;
1634 rc = VINF_SUCCESS;
1635 break;
1636 }
1637 }
1638 }
1639 return VINF_SUCCESS;
1640}
1641
1642
1643/**
1644 * Implements GUEST_SESSION_ACCEPT.
1645 *
1646 * @returns VBox status code.
1647 * @retval VINF_HGCM_ASYNC_EXECUTE on success as we complete the message.
1648 * @retval VERR_NOT_FOUND if the specified session ID wasn't found.
1649 * @retval VERR_MISMATCH if the key didn't match.
1650 * @retval VERR_ACCESS_DENIED if we're in legacy mode or is master.
1651 * @retval VERR_RESOURCE_BUSY if the client is already associated with a
1652 * session.
1653 *
1654 * @param idClient The client's ID.
1655 * @param hCall The call handle for completing it.
1656 * @param cParms Number of parameters.
1657 * @param paParms The parameters.
1658 */
1659int GstCtrlService::clientSessionAccept(uint32_t idClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1660{
1661 /*
1662 * Validate parameters.
1663 */
1664 ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
1665 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1666 uint32_t const idSession = paParms[0].u.uint32;
1667 ASSERT_GUEST_RETURN(idSession >= 1, VERR_OUT_OF_RANGE);
1668 ASSERT_GUEST_RETURN(idSession <= 0xfff0, VERR_OUT_OF_RANGE);
1669
1670 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_PTR, VERR_WRONG_PARAMETER_TYPE);
1671 uint32_t const cbKey = paParms[1].u.pointer.size;
1672 void const *pvKey = paParms[1].u.pointer.addr;
1673 ASSERT_GUEST_RETURN(cbKey >= 64, VERR_BUFFER_UNDERFLOW);
1674 ASSERT_GUEST_RETURN(cbKey <= _16K, VERR_BUFFER_OVERFLOW);
1675
1676 ClientStateMapIter ItClientState = mClientStateMap.find(idClient);
1677 ASSERT_GUEST_MSG_RETURN(ItClientState != mClientStateMap.end(), ("idClient=%RU32\n", idClient), VERR_INVALID_CLIENT_ID);
1678 ClientState &rClientState = ItClientState->second;
1679 ASSERT_GUEST_RETURN(!rClientState.m_fIsMaster, VERR_ACCESS_DENIED);
1680 ASSERT_GUEST_RETURN(!m_fLegacyMode, VERR_ACCESS_DENIED);
1681 Assert(m_idMasterClient != idClient);
1682 ASSERT_GUEST_RETURN(rClientState.m_idSession == UINT32_MAX, VERR_RESOURCE_BUSY);
1683
1684 /*
1685 * Look for the specified session and match the key to it.
1686 */
1687 GstCtrlPreparedSession *pCur;
1688 RTListForEach(&m_PreparedSessions, pCur, GstCtrlPreparedSession, ListEntry)
1689 {
1690 if (pCur->idSession == idSession)
1691 {
1692 if ( pCur->cbKey == cbKey
1693 && memcmp(pCur->abKey, pvKey, cbKey) == 0)
1694 {
1695 /*
1696 * We've got a match. Try complete the request and
1697 */
1698 int rc = mpHelpers->pfnCallComplete(hCall, VERR_NOT_FOUND);
1699 if (RT_SUCCESS(rc))
1700 {
1701 rClientState.m_idSession = idSession;
1702
1703 RTMemFree(pCur);
1704 m_cPreparedSessions -= 1;
1705 }
1706 else
1707 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
1708 return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
1709 }
1710 LogFunc(("Key mismatch for %u!\n", idClient));
1711 return VERR_MISMATCH;
1712 }
1713 }
1714
1715 LogFunc(("No client prepared for %u!\n", idClient));
1716 return VERR_NOT_FOUND;
1717}
1718
1719
1720/**
1721 * Client asks another client (guest) session to close.
1722 *
1723 * @return IPRT status code.
1724 * @param idClient The client's ID.
1725 * @param cParms Number of parameters.
1726 * @param paParms Array of parameters.
1727 */
1728int GstCtrlService::clientSessionCloseOther(uint32_t idClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1729{
1730 /*
1731 * Validate input.
1732 */
1733 ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
1734 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1735 uint32_t const idContext = paParms[0].u.uint32;
1736
1737 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1738 uint32_t const fFlags = paParms[1].u.uint32;
1739
1740 ClientStateMapIter ItClientState = mClientStateMap.find(idClient);
1741 ASSERT_GUEST_MSG_RETURN(ItClientState != mClientStateMap.end(), ("idClient=%RU32\n", idClient), VERR_INVALID_CLIENT_ID);
1742 ClientState &rClientState = ItClientState->second;
1743 ASSERT_GUEST_RETURN(rClientState.m_fIsMaster, VERR_ACCESS_DENIED);
1744
1745 /*
1746 * Forward the command to the destiation.
1747 * Since we modify the first parameter, we must make a copy of the parameters.
1748 */
1749 VBOXHGCMSVCPARM aParms[2];
1750 HGCMSvcSetU64(&aParms[0], idContext | VBOX_GUESTCTRL_DST_SESSION);
1751 HGCMSvcSetU32(&aParms[1], fFlags);
1752 int rc = hostProcessCommand(HOST_SESSION_CLOSE, RT_ELEMENTS(aParms), aParms);
1753
1754 LogFlowFunc(("Closing guest context ID=%RU32 (from client ID=%RU32) returned with rc=%Rrc\n", idContext, idClient, rc));
1755 return rc;
1756}
1757
1758
1759/**
1760 * For compatiblity with old additions only - filtering / set session ID.
1761 *
1762 * @return VBox status code.
1763 * @param idClient The client's HGCM ID.
1764 * @param cParms Number of parameters.
1765 * @param paParms Array of parameters.
1766 */
1767int GstCtrlService::clientMsgOldFilterSet(uint32_t idClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1768{
1769 /*
1770 * Validate input and access.
1771 */
1772 ASSERT_GUEST_RETURN(cParms == 4, VERR_WRONG_PARAMETER_COUNT);
1773 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1774 uint32_t uValue = paParms[0].u.uint32;
1775 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1776 uint32_t fMaskAdd = paParms[1].u.uint32;
1777 ASSERT_GUEST_RETURN(paParms[2].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1778 uint32_t fMaskRemove = paParms[2].u.uint32;
1779 ASSERT_GUEST_RETURN(paParms[3].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /* flags, unused */
1780
1781 ClientStateMapIter ItClientState = mClientStateMap.find(idClient);
1782 ASSERT_GUEST_MSG_RETURN(ItClientState != mClientStateMap.end(), ("idClient=%RU32\n", idClient), VERR_INVALID_CLIENT_ID);
1783 ClientState &rClientState = ItClientState->second;
1784
1785 /*
1786 * We have a bunch of expectations here:
1787 * - Never called in non-legacy mode.
1788 * - Only called once per session.
1789 * - Never called by the master session.
1790 * - Clients that doesn't wish for any messages passes all zeros.
1791 * - All other calls has a unique session ID.
1792 */
1793 ASSERT_GUEST_LOGREL_RETURN(m_fLegacyMode, VERR_WRONG_ORDER);
1794 ASSERT_GUEST_LOGREL_MSG_RETURN(rClientState.m_idSession == UINT32_MAX, ("m_idSession=%#x\n", rClientState.m_idSession),
1795 VERR_WRONG_ORDER);
1796 ASSERT_GUEST_LOGREL_RETURN(!rClientState.m_fIsMaster, VERR_WRONG_ORDER);
1797
1798 if (uValue == 0)
1799 {
1800 ASSERT_GUEST_LOGREL(fMaskAdd == 0);
1801 ASSERT_GUEST_LOGREL(fMaskRemove == 0);
1802 /* Nothing to do, already muted (UINT32_MAX). */
1803 }
1804 else
1805 {
1806 ASSERT_GUEST_LOGREL(fMaskAdd == UINT32_C(0xf8000000));
1807 ASSERT_GUEST_LOGREL(fMaskRemove == 0);
1808
1809 uint32_t idSession = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uValue);
1810 ASSERT_GUEST_LOGREL_MSG_RETURN(idSession > 0, ("idSession=%u (%#x)\n", idSession, uValue), VERR_OUT_OF_RANGE);
1811
1812 for (ClientStateMapIter It = mClientStateMap.begin(); It != mClientStateMap.end(); ++It)
1813 ASSERT_GUEST_LOGREL_MSG_RETURN(It->second.m_idSession != idSession,
1814 ("idSession=%u uValue=%#x idClient=%u; conflicting with client %u\n",
1815 idSession, uValue, idClient, It->second.mID),
1816 VERR_DUPLICATE);
1817 /* Commit it. */
1818 rClientState.m_idSession = idSession;
1819 }
1820 return VINF_SUCCESS;
1821}
1822
1823
1824/**
1825 * For compatibility with old additions only - skip the current command w/o
1826 * calling main code.
1827 *
1828 * Please note that we don't care if the caller cancelled the request, because
1829 * old additions code didn't give damn about VERR_INTERRUPT.
1830 *
1831 * @return VBox status code.
1832 * @param idClient The client's HGCM ID.
1833 * @param cParms Number of parameters.
1834 */
1835int GstCtrlService::clientMsgOldSkip(uint32_t idClient, uint32_t cParms)
1836{
1837 /*
1838 * Validate input and access.
1839 */
1840 ASSERT_GUEST_RETURN(cParms == 1, VERR_WRONG_PARAMETER_COUNT);
1841
1842 ClientStateMapIter ItClientState = mClientStateMap.find(idClient);
1843 ASSERT_GUEST_MSG_RETURN(ItClientState != mClientStateMap.end(), ("idClient=%RU32\n", idClient), VERR_INVALID_CLIENT_ID);
1844 ClientState &rClientState = ItClientState->second;
1845
1846 /*
1847 * Execute the request.
1848 */
1849 if (!rClientState.mHostCmdList.empty())
1850 rClientState.OldDitchFirstHostCmd();
1851
1852 LogFlowFunc(("[Client %RU32] Skipped current message - leagcy function\n", idClient));
1853 return VINF_SUCCESS;
1854}
1855
1856
1857/**
1858 * Notifies the host (using low-level HGCM callbacks) about an event
1859 * which was sent from the client.
1860 *
1861 * @return IPRT status code.
1862 * @param idFunction Function (event) that occured.
1863 * @param cParms Number of parameters.
1864 * @param paParms Array of parameters.
1865 */
1866int GstCtrlService::hostCallback(uint32_t idFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1867{
1868 LogFlowFunc(("idFunction=%u (%s), cParms=%ld, paParms=%p\n", idFunction, GstCtrlGuestFnName((eGuestFn)idFunction), cParms, paParms));
1869
1870 int rc;
1871 if (mpfnHostCallback)
1872 {
1873 VBOXGUESTCTRLHOSTCALLBACK data(cParms, paParms);
1874 /** @todo Not sure if this try/catch is necessary, I pushed it down here from
1875 * GstCtrlService::call where it was not needed for anything else that I
1876 * could spot. I know this might be a tough, but I expect someone writing
1877 * this kind of code to know what can throw errors and handle them where it
1878 * is appropriate, rather than grand catch-all-at-the-top crap like this.
1879 * The reason why it is utter crap, is that you have no state cleanup code
1880 * where you might need it, which is why I despise exceptions in general */
1881 try
1882 {
1883 rc = mpfnHostCallback(mpvHostData, idFunction, (void *)(&data), sizeof(data));
1884 }
1885 catch (std::bad_alloc &)
1886 {
1887 rc = VERR_NO_MEMORY;
1888 }
1889 }
1890 else
1891 rc = VERR_NOT_SUPPORTED;
1892
1893 LogFlowFunc(("Returning rc=%Rrc\n", rc));
1894 return rc;
1895}
1896
1897
1898/**
1899 * Processes a command received from the host side and re-routes it to
1900 * a connect client on the guest.
1901 *
1902 * @return IPRT status code.
1903 * @param idFunction Function code to process.
1904 * @param cParms Number of parameters.
1905 * @param paParms Array of parameters.
1906 */
1907int GstCtrlService::hostProcessCommand(uint32_t idFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1908{
1909 /*
1910 * If no client is connected at all we don't buffer any host commands
1911 * and immediately return an error to the host. This avoids the host
1912 * waiting for a response from the guest side in case VBoxService on
1913 * the guest is not running/system is messed up somehow.
1914 */
1915 if (mClientStateMap.empty())
1916 {
1917 LogFlow(("GstCtrlService::hostProcessCommand: VERR_NOT_FOUND!\n"));
1918 return VERR_NOT_FOUND;
1919 }
1920
1921 HostCommand *pHostCmd = new (std::nothrow) HostCommand();
1922 AssertReturn(pHostCmd, VERR_NO_MEMORY);
1923
1924 int rc = pHostCmd->Init(idFunction, cParms, paParms);
1925 if (RT_SUCCESS(rc))
1926 {
1927 RTListAppend(&mHostCmdList, &pHostCmd->m_ListEntry);
1928 LogFlowFunc(("Handling host command m_idContextAndDst=%#RX64, idFunction=%RU32, cParms=%RU32, paParms=%p, cClients=%zu\n",
1929 pHostCmd->m_idContextAndDst, idFunction, cParms, paParms, mClientStateMap.size()));
1930
1931 /*
1932 * Find the message destination and post it to the client. If the
1933 * session ID doesn't match any particular client it goes to the master.
1934 */
1935 AssertMsg(!mClientStateMap.empty(), ("Client state map is empty when it should not be!\n"));
1936
1937 /* Dispatch to the session. */
1938 if (pHostCmd->m_idContextAndDst & VBOX_GUESTCTRL_DST_SESSION)
1939 {
1940 rc = VWRN_NOT_FOUND;
1941 uint32_t const idSession = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pHostCmd->m_idContext);
1942 for (ClientStateMapIter It = mClientStateMap.begin(); It != mClientStateMap.end(); ++It)
1943 {
1944 ClientState &rClientState = It->second;
1945 if (rClientState.m_idSession == idSession)
1946 {
1947 rc = rClientState.EnqueueCommand(pHostCmd);
1948 if (RT_SUCCESS(rc))
1949 {
1950 int rc2 = rClientState.Wakeup();
1951 LogFlowFunc(("Woke up client ID=%RU32 -> rc=%Rrc\n", rClientState.mID, rc2));
1952 RT_NOREF(rc2);
1953 }
1954 break;
1955 }
1956 }
1957 }
1958
1959 /* Does the message go to the root service? */
1960 if ( (pHostCmd->m_idContextAndDst & VBOX_GUESTCTRL_DST_ROOT_SVC)
1961 && RT_SUCCESS(rc))
1962 {
1963 ClientStateMapIter It = mClientStateMap.find(m_idMasterClient);
1964 if (It != mClientStateMap.end())
1965 {
1966 ClientState &rClientState = It->second;
1967 int rc2 = rClientState.EnqueueCommand(pHostCmd);
1968 if (RT_SUCCESS(rc2))
1969 {
1970 rc2 = rClientState.Wakeup();
1971 LogFlowFunc(("Woke up client ID=%RU32 (master) -> rc=%Rrc\n", rClientState.mID, rc2));
1972 }
1973 else
1974 rc = rc2;
1975 }
1976 else
1977 rc = VERR_NOT_FOUND;
1978 }
1979 }
1980
1981 /* Drop our command reference. */
1982 pHostCmd->SaneRelease();
1983
1984 if (RT_FAILURE(rc))
1985 LogFunc(("Failed %Rrc (idFunction=%u, cParms=%u)\n", rc, idFunction, cParms));
1986 return rc;
1987}
1988
1989
1990/**
1991 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnHostCall,
1992 * Wraps to the hostProcessCommand() member function.}
1993 */
1994/*static*/ DECLCALLBACK(int)
1995GstCtrlService::svcHostCall(void *pvService, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1996{
1997 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
1998 SELF *pThis = reinterpret_cast<SELF *>(pvService);
1999 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2000
2001 LogFlowFunc(("fn=%RU32, cParms=%RU32, paParms=0x%p\n", u32Function, cParms, paParms));
2002 AssertReturn(u32Function != HOST_CANCEL_PENDING_WAITS, VERR_INVALID_FUNCTION);
2003 return pThis->hostProcessCommand(u32Function, cParms, paParms);
2004}
2005
2006
2007/**
2008 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnCall}
2009 *
2010 * @note All functions which do not involve an unreasonable delay will be
2011 * handled synchronously. If needed, we will add a request handler
2012 * thread in future for those which do.
2013 * @thread HGCM
2014 */
2015/*static*/ DECLCALLBACK(void)
2016GstCtrlService::svcCall(void *pvService, VBOXHGCMCALLHANDLE hCall, uint32_t idClient, void *pvClient,
2017 uint32_t idFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[], uint64_t tsArrival)
2018{
2019 LogFlowFunc(("[Client %RU32] idFunction=%RU32 (%s), cParms=%RU32, paParms=0x%p\n",
2020 idClient, idFunction, GstCtrlHostFnName((eHostFn)idFunction), cParms, paParms));
2021 RT_NOREF(tsArrival, pvClient);
2022
2023 AssertLogRelReturnVoid(VALID_PTR(pvService));
2024 SELF *pThis= reinterpret_cast<SELF *>(pvService);
2025 AssertPtrReturnVoid(pThis);
2026
2027 int rc;
2028 switch (idFunction)
2029 {
2030 case GUEST_MAKE_ME_MASTER:
2031 LogFlowFunc(("[Client %RU32] GUEST_MAKE_ME_MASTER\n", idClient));
2032 rc = pThis->clientMakeMeMaster(idClient, hCall, cParms);
2033 break;
2034 case GUEST_MSG_PEEK_NOWAIT:
2035 LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_NOWAIT\n", idClient));
2036 rc = pThis->clientMsgPeek(idClient, hCall, cParms, paParms, false /*fWait*/);
2037 break;
2038 case GUEST_MSG_PEEK_WAIT:
2039 LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_WAIT\n", idClient));
2040 rc = pThis->clientMsgPeek(idClient, hCall, cParms, paParms, true /*fWait*/);
2041 break;
2042 case GUEST_MSG_GET:
2043 LogFlowFunc(("[Client %RU32] GUEST_MSG_GET\n", idClient));
2044 rc = pThis->clientMsgGet(idClient, hCall, cParms, paParms);
2045 break;
2046 case GUEST_MSG_CANCEL:
2047 LogFlowFunc(("[Client %RU32] GUEST_MSG_CANCEL\n", idClient));
2048 rc = pThis->clientMsgCancel(idClient, cParms);
2049 break;
2050 case GUEST_MSG_SKIP:
2051 LogFlowFunc(("[Client %RU32] GUEST_MSG_SKIP\n", idClient));
2052 rc = pThis->clientMsgSkip(idClient, hCall, cParms);
2053 break;
2054 case GUEST_SESSION_PREPARE:
2055 LogFlowFunc(("[Client %RU32] GUEST_SESSION_PREPARE\n", idClient));
2056 rc = pThis->clientSessionPrepare(idClient, hCall, cParms, paParms);
2057 break;
2058 case GUEST_SESSION_CANCEL_PREPARED:
2059 LogFlowFunc(("[Client %RU32] GUEST_SESSION_CANCEL_PREPARED\n", idClient));
2060 rc = pThis->clientSessionCancelPrepared(idClient, cParms, paParms);
2061 break;
2062 case GUEST_SESSION_ACCEPT:
2063 LogFlowFunc(("[Client %RU32] GUEST_SESSION_ACCEPT\n", idClient));
2064 rc = pThis->clientSessionAccept(idClient, hCall, cParms, paParms);
2065 break;
2066 case GUEST_SESSION_CLOSE:
2067 LogFlowFunc(("[Client %RU32] GUEST_SESSION_CLOSE\n", idClient));
2068 rc = pThis->clientSessionCloseOther(idClient, cParms, paParms);
2069 break;
2070
2071 /*
2072 * Stuff the goes to various main objects:
2073 */
2074 case GUEST_DISCONNECTED:
2075 case GUEST_MSG_REPLY:
2076 case GUEST_MSG_PROGRESS_UPDATE:
2077 case GUEST_SESSION_NOTIFY:
2078 case GUEST_EXEC_OUTPUT:
2079 case GUEST_EXEC_STATUS:
2080 case GUEST_EXEC_INPUT_STATUS:
2081 case GUEST_EXEC_IO_NOTIFY:
2082 case GUEST_DIR_NOTIFY:
2083 case GUEST_FILE_NOTIFY:
2084 rc = pThis->hostCallback(idFunction, cParms, paParms);
2085 Assert(rc != VINF_HGCM_ASYNC_EXECUTE);
2086 break;
2087
2088 /*
2089 * The remaining commands are here for compatibility with older
2090 * guest additions:
2091 */
2092 case GUEST_MSG_WAIT:
2093 LogFlowFunc(("[Client %RU32] GUEST_MSG_WAIT\n", idClient));
2094 pThis->clientMsgOldGet(idClient, hCall, cParms, paParms);
2095 rc = VINF_HGCM_ASYNC_EXECUTE;
2096 break;
2097
2098 case GUEST_MSG_SKIP_OLD:
2099 LogFlowFunc(("[Client %RU32] GUEST_MSG_SKIP_OLD\n", idClient));
2100 rc = pThis->clientMsgOldSkip(idClient, cParms);
2101 break;
2102
2103 case GUEST_MSG_FILTER_SET:
2104 LogFlowFunc(("[Client %RU32] GUEST_MSG_FILTER_SET\n", idClient));
2105 rc = pThis->clientMsgOldFilterSet(idClient, cParms, paParms);
2106 break;
2107
2108 case GUEST_MSG_FILTER_UNSET:
2109 LogFlowFunc(("[Client %RU32] GUEST_MSG_FILTER_UNSET\n", idClient));
2110 rc = VERR_NOT_IMPLEMENTED;
2111 break;
2112
2113 /*
2114 * Anything else shall return fail with invalid function.
2115 *
2116 * Note! We used to return VINF_SUCCESS for these. See bugref:9313
2117 * and Guest::i_notifyCtrlDispatcher().
2118 */
2119 default:
2120 ASSERT_GUEST_MSG_FAILED(("idFunction=%d (%#x)\n", idFunction, idFunction));
2121 rc = VERR_INVALID_FUNCTION;
2122 break;
2123 }
2124
2125 if (rc != VINF_HGCM_ASYNC_EXECUTE)
2126 {
2127 /* Tell the client that the call is complete (unblocks waiting). */
2128 LogFlowFunc(("[Client %RU32] Calling pfnCallComplete w/ rc=%Rrc\n", idClient, rc));
2129 AssertPtr(pThis->mpHelpers);
2130 pThis->mpHelpers->pfnCallComplete(hCall, rc);
2131 }
2132}
2133
2134
2135/**
2136 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnRegisterExtension,
2137 * Installs a host callback for notifications of property changes.}
2138 */
2139/*static*/ DECLCALLBACK(int) GstCtrlService::svcRegisterExtension(void *pvService, PFNHGCMSVCEXT pfnExtension, void *pvExtension)
2140{
2141 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
2142 SELF *pSelf = reinterpret_cast<SELF *>(pvService);
2143 AssertPtrReturn(pSelf, VERR_INVALID_POINTER);
2144 pSelf->mpfnHostCallback = pfnExtension;
2145 pSelf->mpvHostData = pvExtension;
2146 return VINF_SUCCESS;
2147}
2148
2149
2150/**
2151 * @copydoc VBOXHGCMSVCLOAD
2152 */
2153extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
2154{
2155 int rc = VINF_SUCCESS;
2156
2157 LogFlowFunc(("pTable=%p\n", pTable));
2158
2159 if (!VALID_PTR(pTable))
2160 {
2161 rc = VERR_INVALID_PARAMETER;
2162 }
2163 else
2164 {
2165 LogFlowFunc(("pTable->cbSize=%d, pTable->u32Version=0x%08X\n", pTable->cbSize, pTable->u32Version));
2166
2167 if ( pTable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
2168 || pTable->u32Version != VBOX_HGCM_SVC_VERSION)
2169 {
2170 rc = VERR_VERSION_MISMATCH;
2171 }
2172 else
2173 {
2174 GstCtrlService *pService = NULL;
2175 /* No exceptions may propagate outside. */
2176 try
2177 {
2178 pService = new GstCtrlService(pTable->pHelpers);
2179 }
2180 catch (int rcThrown)
2181 {
2182 rc = rcThrown;
2183 }
2184 catch(std::bad_alloc &)
2185 {
2186 rc = VERR_NO_MEMORY;
2187 }
2188
2189 if (RT_SUCCESS(rc))
2190 {
2191 /*
2192 * We don't need an additional client data area on the host,
2193 * because we're a class which can have members for that :-).
2194 */
2195 pTable->cbClient = 0;
2196
2197 /* Register functions. */
2198 pTable->pfnUnload = GstCtrlService::svcUnload;
2199 pTable->pfnConnect = GstCtrlService::svcConnect;
2200 pTable->pfnDisconnect = GstCtrlService::svcDisconnect;
2201 pTable->pfnCall = GstCtrlService::svcCall;
2202 pTable->pfnHostCall = GstCtrlService::svcHostCall;
2203 pTable->pfnSaveState = NULL; /* The GstCtrlService is stateless, so the normal */
2204 pTable->pfnLoadState = NULL; /* construction done before restoring suffices */
2205 pTable->pfnRegisterExtension = GstCtrlService::svcRegisterExtension;
2206
2207 /* Service specific initialization. */
2208 pTable->pvService = pService;
2209 }
2210 else
2211 {
2212 if (pService)
2213 {
2214 delete pService;
2215 pService = NULL;
2216 }
2217 }
2218 }
2219 }
2220
2221 LogFlowFunc(("Returning %Rrc\n", rc));
2222 return rc;
2223}
2224
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