VirtualBox

source: vbox/trunk/src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.cpp@ 85672

Last change on this file since 85672 was 85121, checked in by vboxsync, 4 years ago

iprt/cdefs.h: Refactored the typedef use of DECLCALLBACK as well as DECLCALLBACKMEMBER to wrap the whole expression, similar to the DECLR?CALLBACKMEMBER macros. This allows adding a throw() at the end when compiling with the VC++ compiler to indicate that the callbacks won't throw anything, so we can stop supressing the C5039 warning about passing functions that can potential throw C++ exceptions to extern C code that can't necessarily cope with such (unwind,++). Introduced a few _EX variations that allows specifying different/no calling convention too, as that's handy when dynamically resolving host APIs. Fixed numerous places missing DECLCALLBACK and such. Left two angry @todos regarding use of CreateThread. bugref:9794

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