VirtualBox

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

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

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

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

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