VirtualBox

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

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

GuestControl: Preps for using the pvClient data area and have the mClientStateMap only contain pointers. bugref:9313 [fix]

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