VirtualBox

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

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

Main,VBoxService,GstCtrlSvc: Added functions for exchanging feature masks between guest and host so new features can more easily be added without resorting to version comparsion magic. Added alternative read and write completion notifications that includes the new file offset. Made sure RTFileReadAt and RTFileWriteAt are followed by a RTFileSeek call so we'll end up with the same file position regardless of guest OS. [fixes] bugref:9320

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 96.9 KB
Line 
1/* $Id: VBoxGuestControlSvc.cpp 79298 2019-06-24 10:10:39Z vboxsync $ */
2/** @file
3 * Guest Control Service: Controlling the guest.
4 */
5
6/*
7 * Copyright (C) 2011-2019 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 uint8_t abKey[RT_FLEXIBLE_ARRAY];
846} GstCtrlPreparedSession;
847
848
849/**
850 * Class containing the shared information service functionality.
851 */
852class GstCtrlService : public RTCNonCopyable
853{
854
855private:
856
857 /** Type definition for use in callback functions. */
858 typedef GstCtrlService SELF;
859 /** HGCM helper functions. */
860 PVBOXHGCMSVCHELPERS mpHelpers;
861 /** Callback function supplied by the host for notification of updates to properties. */
862 PFNHGCMSVCEXT mpfnHostCallback;
863 /** User data pointer to be supplied to the host callback function. */
864 void *mpvHostData;
865 /** Map containing all connected clients, key is HGCM client ID. */
866 ClientStateMap m_ClientStateMap;
867 /** Session ID -> client state. */
868 ClientStateMap m_SessionIdMap;
869 /** The current master client, NULL if none. */
870 ClientState *m_pMasterClient;
871 /** The master HGCM client ID, UINT32_MAX if none. */
872 uint32_t m_idMasterClient;
873 /** Set if we're in legacy mode (pre 6.0). */
874 bool m_fLegacyMode;
875 /** Number of prepared sessions. */
876 uint32_t m_cPreparedSessions;
877 /** List of prepared session (GstCtrlPreparedSession). */
878 RTLISTANCHOR m_PreparedSessions;
879 /** Guest feature flags, VBOX_GUESTCTRL_GF_0_XXX. */
880 uint64_t m_fGuestFeatures0;
881 /** Guest feature flags, VBOX_GUESTCTRL_GF_1_XXX. */
882 uint64_t m_fGuestFeatures1;
883
884public:
885 explicit GstCtrlService(PVBOXHGCMSVCHELPERS pHelpers)
886 : mpHelpers(pHelpers)
887 , mpfnHostCallback(NULL)
888 , mpvHostData(NULL)
889 , m_pMasterClient(NULL)
890 , m_idMasterClient(UINT32_MAX)
891 , m_fLegacyMode(true)
892 , m_cPreparedSessions(0)
893 , m_fGuestFeatures0(0)
894 , m_fGuestFeatures1(0)
895 {
896 RTListInit(&m_PreparedSessions);
897 }
898
899 static DECLCALLBACK(int) svcUnload(void *pvService);
900 static DECLCALLBACK(int) svcConnect(void *pvService, uint32_t idClient, void *pvClient,
901 uint32_t fRequestor, bool fRestoring);
902 static DECLCALLBACK(int) svcDisconnect(void *pvService, uint32_t idClient, void *pvClient);
903 static DECLCALLBACK(void) svcCall(void *pvService, VBOXHGCMCALLHANDLE hCall, uint32_t idClient, void *pvClient,
904 uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[], uint64_t tsArrival);
905 static DECLCALLBACK(int) svcHostCall(void *pvService, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
906 static DECLCALLBACK(int) svcSaveState(void *pvService, uint32_t idClient, void *pvClient, PSSMHANDLE pSSM);
907 static DECLCALLBACK(int) svcLoadState(void *pvService, uint32_t idClient, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion);
908 static DECLCALLBACK(int) svcRegisterExtension(void *pvService, PFNHGCMSVCEXT pfnExtension, void *pvExtension);
909
910private:
911 int clientMakeMeMaster(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms);
912 int clientReportFeatures(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
913 int clientQueryFeatures(VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
914 int clientMsgPeek(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool fWait);
915 int clientMsgGet(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
916 int clientMsgCancel(ClientState *pClient, uint32_t cParms);
917 int clientMsgSkip(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
918 int clientSessionPrepare(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
919 int clientSessionCancelPrepared(ClientState *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
920 int clientSessionAccept(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
921 int clientSessionCloseOther(ClientState *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
922 int clientToMain(ClientState *pClient, uint32_t idMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
923
924 int clientMsgOldGet(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
925 int clientMsgOldFilterSet(ClientState *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
926 int clientMsgOldSkip(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms);
927
928 int hostCallback(uint32_t idMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
929 int hostProcessMessage(uint32_t idMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
930
931 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(GstCtrlService);
932};
933
934
935/** Host feature mask for GUEST_MSG_REPORT_FEATURES/GUEST_MSG_QUERY_FEATURES. */
936static uint64_t const g_fGstCtrlHostFeatures0 = VBOX_GUESTCTRL_HF_0_NOTIFY_RDWR_OFFSET;
937
938
939
940/**
941 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnUnload,
942 * Simply deletes the GstCtrlService object}
943 */
944/*static*/ DECLCALLBACK(int)
945GstCtrlService::svcUnload(void *pvService)
946{
947 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
948 SELF *pThis = reinterpret_cast<SELF *>(pvService);
949 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
950
951 delete pThis;
952
953 return VINF_SUCCESS;
954}
955
956
957
958/**
959 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnConnect,
960 * Initializes the state for a new client.}
961 */
962/*static*/ DECLCALLBACK(int)
963GstCtrlService::svcConnect(void *pvService, uint32_t idClient, void *pvClient, uint32_t fRequestor, bool fRestoring)
964{
965 LogFlowFunc(("[Client %RU32] Connected\n", idClient));
966
967 RT_NOREF(fRestoring, pvClient);
968 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
969 SELF *pThis = reinterpret_cast<SELF *>(pvService);
970 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
971
972 AssertMsg(pThis->m_ClientStateMap.find(idClient) == pThis->m_ClientStateMap.end(),
973 ("Client with ID=%RU32 already connected when it should not\n", idClient));
974
975 /*
976 * Create client state.
977 */
978 ClientState *pClient = NULL;
979 try
980 {
981 pClient = new (pvClient) ClientState(pThis->mpHelpers, idClient);
982 pThis->m_ClientStateMap[idClient] = pClient;
983 }
984 catch (std::bad_alloc &)
985 {
986 if (pClient)
987 pClient->~ClientState();
988 return VERR_NO_MEMORY;
989 }
990
991 /*
992 * For legacy compatibility reasons we have to pick a master client at some
993 * point, so if the /dev/vboxguest requirements checks out we pick the first
994 * one through the door.
995 */
996/** @todo make picking the master more dynamic/flexible? */
997 if ( pThis->m_fLegacyMode
998 && pThis->m_idMasterClient == UINT32_MAX)
999 {
1000 if ( fRequestor == VMMDEV_REQUESTOR_LEGACY
1001 || !(fRequestor & VMMDEV_REQUESTOR_USER_DEVICE))
1002 {
1003 LogFunc(("Picking %u as master for now.\n", idClient));
1004 pThis->m_pMasterClient = pClient;
1005 pThis->m_idMasterClient = idClient;
1006 pClient->m_fIsMaster = true;
1007 }
1008 }
1009
1010 return VINF_SUCCESS;
1011}
1012
1013
1014/**
1015 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnConnect,
1016 * Handles a client which disconnected.}
1017 *
1018 * This functiond does some internal cleanup as well as sends notifications to
1019 * the host so that the host can do the same (if required).
1020 */
1021/*static*/ DECLCALLBACK(int)
1022GstCtrlService::svcDisconnect(void *pvService, uint32_t idClient, void *pvClient)
1023{
1024 SELF *pThis = reinterpret_cast<SELF *>(pvService);
1025 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1026 ClientState *pClient = reinterpret_cast<ClientState *>(pvClient);
1027 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
1028 LogFlowFunc(("[Client %RU32] Disconnected (%zu clients total)\n", idClient, pThis->m_ClientStateMap.size()));
1029
1030 /*
1031 * Cancel all pending host messages, replying with GUEST_DISCONNECTED if final recipient.
1032 */
1033 HostMsg *pCurMsg, *pNextMsg;
1034 RTListForEachSafeCpp(&pClient->m_HostMsgList, pCurMsg, pNextMsg, HostMsg, m_ListEntry)
1035 {
1036 RTListNodeRemove(&pCurMsg->m_ListEntry);
1037
1038 VBOXHGCMSVCPARM Parm;
1039 HGCMSvcSetU32(&Parm, pCurMsg->m_idContext);
1040 int rc2 = pThis->hostCallback(GUEST_MSG_DISCONNECTED, 1, &Parm);
1041 LogFlowFunc(("Cancelled host message %u (%s) with idContext=%#x -> %Rrc\n",
1042 pCurMsg->mType, GstCtrlHostMsgtoStr((eHostMsg)pCurMsg->mType), pCurMsg->m_idContext, rc2));
1043 RT_NOREF(rc2);
1044
1045 pCurMsg->Delete();
1046 }
1047
1048 /*
1049 * Delete the client state.
1050 */
1051 pThis->m_ClientStateMap.erase(idClient);
1052 if (pClient->m_idSession != UINT32_MAX)
1053 pThis->m_SessionIdMap.erase(pClient->m_idSession);
1054 pClient->~ClientState();
1055
1056 /*
1057 * If it's the master disconnecting, we need to reset related globals.
1058 */
1059 if (idClient == pThis->m_idMasterClient)
1060 {
1061 pThis->m_pMasterClient = NULL;
1062 pThis->m_idMasterClient = UINT32_MAX;
1063
1064 GstCtrlPreparedSession *pCur, *pNext;
1065 RTListForEachSafe(&pThis->m_PreparedSessions, pCur, pNext, GstCtrlPreparedSession, ListEntry)
1066 {
1067 RTListNodeRemove(&pCur->ListEntry);
1068 RTMemFree(pCur);
1069 }
1070 pThis->m_cPreparedSessions = 0;
1071 }
1072 else
1073 Assert(pClient != pThis->m_pMasterClient);
1074
1075 if (pThis->m_ClientStateMap.empty())
1076 pThis->m_fLegacyMode = true;
1077
1078 return VINF_SUCCESS;
1079}
1080
1081
1082/**
1083 * A client asks for the next message to process.
1084 *
1085 * This either fills in a pending host message into the client's parameter space
1086 * or defers the guest call until we have something from the host.
1087 *
1088 * @returns VBox status code.
1089 * @param pClient The client state.
1090 * @param hCall The client's call handle.
1091 * @param cParms Number of parameters.
1092 * @param paParms Array of parameters.
1093 */
1094int GstCtrlService::clientMsgOldGet(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1095{
1096 ASSERT_GUEST(pClient->m_idSession != UINT32_MAX || pClient->m_fIsMaster || pClient->m_fRestored);
1097
1098 /* Use the current (inbound) connection. */
1099 ClientRequest thisCon;
1100 thisCon.mHandle = hCall;
1101 thisCon.mNumParms = cParms;
1102 thisCon.mParms = paParms;
1103
1104 return pClient->OldRunCurrent(&thisCon);
1105}
1106
1107
1108/**
1109 * Implements GUEST_MAKE_ME_MASTER.
1110 *
1111 * @returns VBox status code.
1112 * @retval VINF_HGCM_ASYNC_EXECUTE on success (we complete the message here).
1113 * @retval VERR_ACCESS_DENIED if not using main VBoxGuest device not
1114 * @retval VERR_RESOURCE_BUSY if there is already a master.
1115 * @retval VERR_VERSION_MISMATCH if VBoxGuest didn't supply requestor info.
1116 * @retval VERR_WRONG_PARAMETER_COUNT
1117 *
1118 * @param pClient The client state.
1119 * @param hCall The client's call handle.
1120 * @param cParms Number of parameters.
1121 */
1122int GstCtrlService::clientMakeMeMaster(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms)
1123{
1124 /*
1125 * Validate the request.
1126 */
1127 ASSERT_GUEST_RETURN(cParms == 0, VERR_WRONG_PARAMETER_COUNT);
1128
1129 uint32_t fRequestor = mpHelpers->pfnGetRequestor(hCall);
1130 ASSERT_GUEST_LOGREL_MSG_RETURN(fRequestor != VMMDEV_REQUESTOR_LEGACY,
1131 ("Outdated VBoxGuest w/o requestor support. Please update!\n"),
1132 VERR_VERSION_MISMATCH);
1133 ASSERT_GUEST_LOGREL_MSG_RETURN(!(fRequestor & VMMDEV_REQUESTOR_USER_DEVICE), ("fRequestor=%#x\n", fRequestor),
1134 VERR_ACCESS_DENIED);
1135
1136 /*
1137 * Do the work.
1138 */
1139 ASSERT_GUEST_MSG_RETURN(m_idMasterClient == pClient->m_idClient || m_idMasterClient == UINT32_MAX,
1140 ("Already have master session %RU32, refusing %RU32.\n", m_idMasterClient, pClient->m_idClient),
1141 VERR_RESOURCE_BUSY);
1142 int rc = mpHelpers->pfnCallComplete(hCall, VINF_SUCCESS);
1143 if (RT_SUCCESS(rc))
1144 {
1145 m_pMasterClient = pClient;
1146 m_idMasterClient = pClient->m_idClient;
1147 m_fLegacyMode = false;
1148 pClient->m_fIsMaster = true;
1149 Log(("[Client %RU32] is master.\n", pClient->m_idClient));
1150 }
1151 else
1152 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
1153
1154 return VINF_HGCM_ASYNC_EXECUTE;
1155}
1156
1157
1158/**
1159 * Implements GUEST_MSG_REPORT_FEATURES.
1160 *
1161 * @returns VBox status code.
1162 * @retval VINF_HGCM_ASYNC_EXECUTE on success (we complete the message here).
1163 * @retval VERR_ACCESS_DENIED if not master
1164 * @retval VERR_INVALID_PARAMETER if bit 63 in the 2nd parameter isn't set.
1165 * @retval VERR_WRONG_PARAMETER_COUNT
1166 *
1167 * @param pClient The client state.
1168 * @param hCall The client's call handle.
1169 * @param cParms Number of parameters.
1170 * @param paParms Array of parameters.
1171 */
1172int GstCtrlService::clientReportFeatures(ClientState *pClient, VBOXHGCMCALLHANDLE hCall,
1173 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1174{
1175 /*
1176 * Validate the request.
1177 */
1178 ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
1179 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
1180 uint64_t const fFeatures0 = paParms[0].u.uint64;
1181 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
1182 uint64_t const fFeatures1 = paParms[1].u.uint64;
1183 ASSERT_GUEST_RETURN(fFeatures1 & VBOX_GUESTCTRL_GF_1_MUST_BE_ONE, VERR_INVALID_PARAMETER);
1184
1185 ASSERT_GUEST_RETURN(pClient->m_fIsMaster, VERR_ACCESS_DENIED);
1186
1187 /*
1188 * Do the work.
1189 */
1190 VBOXHGCMSVCPARM aCopyForMain[2] = { paParms[0], paParms[1] };
1191
1192 paParms[0].u.uint64 = g_fGstCtrlHostFeatures0;
1193 paParms[1].u.uint64 = 0;
1194
1195 int rc = mpHelpers->pfnCallComplete(hCall, VINF_SUCCESS);
1196 if (RT_SUCCESS(rc))
1197 {
1198 m_fGuestFeatures0 = fFeatures0;
1199 m_fGuestFeatures1 = fFeatures1;
1200 Log(("[Client %RU32] features: %#RX64 %#RX64\n", pClient->m_idClient, fFeatures0, fFeatures1));
1201
1202 /*
1203 * Forward the info to main.
1204 */
1205 hostCallback(GUEST_MSG_REPORT_FEATURES, RT_ELEMENTS(aCopyForMain), aCopyForMain);
1206 }
1207 else
1208 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
1209
1210 return VINF_HGCM_ASYNC_EXECUTE;
1211}
1212
1213
1214/**
1215 * Implements GUEST_MSG_QUERY_FEATURES.
1216 *
1217 * @returns VBox status code.
1218 * @retval VINF_HGCM_ASYNC_EXECUTE on success (we complete the message here).
1219 * @retval VERR_WRONG_PARAMETER_COUNT
1220 *
1221 * @param hCall The client's call handle.
1222 * @param cParms Number of parameters.
1223 * @param paParms Array of parameters.
1224 */
1225int GstCtrlService::clientQueryFeatures(VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1226{
1227 /*
1228 * Validate the request.
1229 */
1230 ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
1231 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
1232 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
1233 ASSERT_GUEST(paParms[1].u.uint64 & RT_BIT_64(63));
1234
1235 /*
1236 * Do the work.
1237 */
1238 paParms[0].u.uint64 = g_fGstCtrlHostFeatures0;
1239 paParms[1].u.uint64 = 0;
1240 int rc = mpHelpers->pfnCallComplete(hCall, VINF_SUCCESS);
1241 if (RT_FAILURE(rc))
1242 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
1243
1244 return VINF_HGCM_ASYNC_EXECUTE;
1245}
1246
1247
1248/**
1249 * Implements GUEST_MSG_PEEK_WAIT and GUEST_MSG_PEEK_NOWAIT.
1250 *
1251 * @returns VBox status code.
1252 * @retval VINF_SUCCESS if a message was pending and is being returned.
1253 * @retval VERR_TRY_AGAIN if no message pending and not blocking.
1254 * @retval VERR_RESOURCE_BUSY if another read already made a waiting call.
1255 * @retval VINF_HGCM_ASYNC_EXECUTE if message wait is pending.
1256 *
1257 * @param pClient The client state.
1258 * @param hCall The client's call handle.
1259 * @param cParms Number of parameters.
1260 * @param paParms Array of parameters.
1261 * @param fWait Set if we should wait for a message, clear if to return
1262 * immediately.
1263 */
1264int GstCtrlService::clientMsgPeek(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool fWait)
1265{
1266 /*
1267 * Validate the request.
1268 */
1269 ASSERT_GUEST_MSG_RETURN(cParms >= 2, ("cParms=%u!\n", cParms), VERR_WRONG_PARAMETER_COUNT);
1270
1271 uint64_t idRestoreCheck = 0;
1272 uint32_t i = 0;
1273 if (paParms[i].type == VBOX_HGCM_SVC_PARM_64BIT)
1274 {
1275 idRestoreCheck = paParms[0].u.uint64;
1276 paParms[0].u.uint64 = 0;
1277 i++;
1278 }
1279 for (; i < cParms; i++)
1280 {
1281 ASSERT_GUEST_MSG_RETURN(paParms[i].type == VBOX_HGCM_SVC_PARM_32BIT, ("#%u type=%u\n", i, paParms[i].type),
1282 VERR_WRONG_PARAMETER_TYPE);
1283 paParms[i].u.uint32 = 0;
1284 }
1285
1286 /*
1287 * Check restore session ID.
1288 */
1289 if (idRestoreCheck != 0)
1290 {
1291 uint64_t idRestore = mpHelpers->pfnGetVMMDevSessionId(mpHelpers);
1292 if (idRestoreCheck != idRestore)
1293 {
1294 paParms[0].u.uint64 = idRestore;
1295 LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_XXXX -> VERR_VM_RESTORED (%#RX64 -> %#RX64)\n",
1296 pClient->m_idClient, idRestoreCheck, idRestore));
1297 return VERR_VM_RESTORED;
1298 }
1299 Assert(!mpHelpers->pfnIsCallRestored(hCall));
1300 }
1301
1302 /*
1303 * Return information about the first message if one is pending in the list.
1304 */
1305 HostMsg *pFirstMsg = RTListGetFirstCpp(&pClient->m_HostMsgList, HostMsg, m_ListEntry);
1306 if (pFirstMsg)
1307 {
1308 pFirstMsg->setPeekReturn(paParms, cParms);
1309 LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_XXXX -> VINF_SUCCESS (idMsg=%u (%s), cParms=%u)\n",
1310 pClient->m_idClient, pFirstMsg->mType, GstCtrlHostMsgtoStr((eHostMsg)pFirstMsg->mType), pFirstMsg->mParmCount));
1311 return VINF_SUCCESS;
1312 }
1313
1314 /*
1315 * If we cannot wait, fail the call.
1316 */
1317 if (!fWait)
1318 {
1319 LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_NOWAIT -> VERR_TRY_AGAIN\n", pClient->m_idClient));
1320 return VERR_TRY_AGAIN;
1321 }
1322
1323 /*
1324 * Wait for the host to queue a message for this client.
1325 */
1326 ASSERT_GUEST_MSG_RETURN(pClient->m_enmPendingMsg == 0, ("Already pending! (idClient=%RU32)\n", pClient->m_idClient),
1327 VERR_RESOURCE_BUSY);
1328 pClient->m_PendingReq.mHandle = hCall;
1329 pClient->m_PendingReq.mNumParms = cParms;
1330 pClient->m_PendingReq.mParms = paParms;
1331 pClient->m_enmPendingMsg = GUEST_MSG_PEEK_WAIT;
1332 LogFlowFunc(("[Client %RU32] Is now in pending mode...\n", pClient->m_idClient));
1333 return VINF_HGCM_ASYNC_EXECUTE;
1334}
1335
1336/**
1337 * Implements GUEST_MSG_GET.
1338 *
1339 * @returns VBox status code.
1340 * @retval VINF_SUCCESS if message retrieved and removed from the pending queue.
1341 * @retval VERR_TRY_AGAIN if no message pending.
1342 * @retval VERR_BUFFER_OVERFLOW if a parmeter buffer is too small. The buffer
1343 * size was updated to reflect the required size, though this isn't yet
1344 * forwarded to the guest. (The guest is better of using peek with
1345 * parameter count + 2 parameters to get the sizes.)
1346 * @retval VERR_MISMATCH if the incoming message ID does not match the pending.
1347 * @retval VINF_HGCM_ASYNC_EXECUTE if message was completed already.
1348 *
1349 * @param pClient The client state.
1350 * @param hCall The client's call handle.
1351 * @param cParms Number of parameters.
1352 * @param paParms Array of parameters.
1353 */
1354int GstCtrlService::clientMsgGet(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1355{
1356 /*
1357 * Validate the request.
1358 *
1359 * The weird first parameter logic is due to GUEST_MSG_WAIT compatibility
1360 * (don't want to rewrite all the message structures).
1361 */
1362 uint32_t const idMsgExpected = cParms > 0 && paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT ? paParms[0].u.uint32
1363 : cParms > 0 && paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT ? paParms[0].u.uint64
1364 : UINT32_MAX;
1365
1366 /*
1367 * Return information about the first message if one is pending in the list.
1368 */
1369 HostMsg *pFirstMsg = RTListGetFirstCpp(&pClient->m_HostMsgList, HostMsg, m_ListEntry);
1370 if (pFirstMsg)
1371 {
1372
1373 ASSERT_GUEST_MSG_RETURN(pFirstMsg->mType == idMsgExpected || idMsgExpected == UINT32_MAX,
1374 ("idMsg=%u (%s) cParms=%u, caller expected %u (%s) and %u\n",
1375 pFirstMsg->mType, GstCtrlHostMsgtoStr((eHostMsg)pFirstMsg->mType), pFirstMsg->mParmCount,
1376 idMsgExpected, GstCtrlHostMsgtoStr((eHostMsg)idMsgExpected), cParms),
1377 VERR_MISMATCH);
1378 ASSERT_GUEST_MSG_RETURN(pFirstMsg->mParmCount == cParms,
1379 ("idMsg=%u (%s) cParms=%u, caller expected %u (%s) and %u\n",
1380 pFirstMsg->mType, GstCtrlHostMsgtoStr((eHostMsg)pFirstMsg->mType), pFirstMsg->mParmCount,
1381 idMsgExpected, GstCtrlHostMsgtoStr((eHostMsg)idMsgExpected), cParms),
1382 VERR_WRONG_PARAMETER_COUNT);
1383
1384 /* Check the parameter types. */
1385 for (uint32_t i = 0; i < cParms; i++)
1386 ASSERT_GUEST_MSG_RETURN(pFirstMsg->mpParms[i].type == paParms[i].type,
1387 ("param #%u: type %u, caller expected %u (idMsg=%u %s)\n", i, pFirstMsg->mpParms[i].type,
1388 paParms[i].type, pFirstMsg->mType, GstCtrlHostMsgtoStr((eHostMsg)pFirstMsg->mType)),
1389 VERR_WRONG_PARAMETER_TYPE);
1390
1391 /*
1392 * Copy out the parameters.
1393 *
1394 * No assertions on buffer overflows, and keep going till the end so we can
1395 * communicate all the required buffer sizes.
1396 */
1397 int rc = VINF_SUCCESS;
1398 for (uint32_t i = 0; i < cParms; i++)
1399 switch (pFirstMsg->mpParms[i].type)
1400 {
1401 case VBOX_HGCM_SVC_PARM_32BIT:
1402 paParms[i].u.uint32 = pFirstMsg->mpParms[i].u.uint32;
1403 break;
1404
1405 case VBOX_HGCM_SVC_PARM_64BIT:
1406 paParms[i].u.uint64 = pFirstMsg->mpParms[i].u.uint64;
1407 break;
1408
1409 case VBOX_HGCM_SVC_PARM_PTR:
1410 {
1411 uint32_t const cbSrc = pFirstMsg->mpParms[i].u.pointer.size;
1412 uint32_t const cbDst = paParms[i].u.pointer.size;
1413 paParms[i].u.pointer.size = cbSrc; /** @todo Check if this is safe in other layers...
1414 * Update: Safe, yes, but VMMDevHGCM doesn't pass it along. */
1415 if (cbSrc <= cbDst)
1416 memcpy(paParms[i].u.pointer.addr, pFirstMsg->mpParms[i].u.pointer.addr, cbSrc);
1417 else
1418 rc = VERR_BUFFER_OVERFLOW;
1419 break;
1420 }
1421
1422 default:
1423 AssertMsgFailed(("#%u: %u\n", i, pFirstMsg->mpParms[i].type));
1424 rc = VERR_INTERNAL_ERROR;
1425 break;
1426 }
1427 if (RT_SUCCESS(rc))
1428 {
1429 /*
1430 * Complete the message and remove the pending message unless the
1431 * guest raced us and cancelled this call in the meantime.
1432 */
1433 AssertPtr(mpHelpers);
1434 rc = mpHelpers->pfnCallComplete(hCall, rc);
1435 if (rc != VERR_CANCELLED)
1436 {
1437 RTListNodeRemove(&pFirstMsg->m_ListEntry);
1438 pFirstMsg->Delete();
1439 }
1440 else
1441 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
1442 return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
1443 }
1444 return rc;
1445 }
1446
1447 paParms[0].u.uint32 = 0;
1448 paParms[1].u.uint32 = 0;
1449 LogFlowFunc(("[Client %RU32] GUEST_MSG_GET -> VERR_TRY_AGAIN\n", pClient->m_idClient));
1450 return VERR_TRY_AGAIN;
1451}
1452
1453/**
1454 * Implements GUEST_MSG_CANCEL.
1455 *
1456 * @returns VBox status code.
1457 * @retval VINF_SUCCESS if cancelled any calls.
1458 * @retval VWRN_NOT_FOUND if no callers.
1459 * @retval VINF_HGCM_ASYNC_EXECUTE if message wait is pending.
1460 *
1461 * @param pClient The client state.
1462 * @param cParms Number of parameters.
1463 */
1464int GstCtrlService::clientMsgCancel(ClientState *pClient, uint32_t cParms)
1465{
1466 /*
1467 * Validate the request.
1468 */
1469 ASSERT_GUEST_MSG_RETURN(cParms == 0, ("cParms=%u!\n", cParms), VERR_WRONG_PARAMETER_COUNT);
1470
1471 /*
1472 * Execute.
1473 */
1474 if (pClient->m_enmPendingMsg != 0)
1475 {
1476 pClient->CancelWaiting();
1477 return VINF_SUCCESS;
1478 }
1479 return VWRN_NOT_FOUND;
1480}
1481
1482
1483/**
1484 * Implements GUEST_MSG_SKIP.
1485 *
1486 * @returns VBox status code.
1487 * @retval VINF_HGCM_ASYNC_EXECUTE on success as we complete the message.
1488 * @retval VERR_NOT_FOUND if no message pending.
1489 *
1490 * @param pClient The client state.
1491 * @param hCall The call handle for completing it.
1492 * @param cParms Number of parameters.
1493 * @param paParms The parameters.
1494 */
1495int GstCtrlService::clientMsgSkip(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1496{
1497 /*
1498 * Validate the call.
1499 */
1500 ASSERT_GUEST_RETURN(cParms <= 2, VERR_WRONG_PARAMETER_COUNT);
1501
1502 int32_t rcSkip = VERR_NOT_SUPPORTED;
1503 if (cParms >= 1)
1504 {
1505 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1506 rcSkip = (int32_t)paParms[0].u.uint32;
1507 }
1508
1509 uint32_t idMsg = UINT32_MAX;
1510 if (cParms >= 2)
1511 {
1512 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1513 idMsg = paParms[1].u.uint32;
1514 }
1515
1516 /*
1517 * Do the job.
1518 */
1519 HostMsg *pFirstMsg = RTListGetFirstCpp(&pClient->m_HostMsgList, HostMsg, m_ListEntry);
1520 if (pFirstMsg)
1521 {
1522 if ( pFirstMsg->mType == idMsg
1523 || idMsg == UINT32_MAX)
1524 {
1525 int rc = mpHelpers->pfnCallComplete(hCall, VINF_SUCCESS);
1526 if (RT_SUCCESS(rc))
1527 {
1528 /*
1529 * Remove the message from the queue.
1530 */
1531 Assert(RTListNodeIsFirst(&pClient->m_HostMsgList, &pFirstMsg->m_ListEntry) );
1532 RTListNodeRemove(&pFirstMsg->m_ListEntry);
1533
1534 /*
1535 * Compose a reply to the host service.
1536 */
1537 VBOXHGCMSVCPARM aReplyParams[5];
1538 HGCMSvcSetU32(&aReplyParams[0], pFirstMsg->m_idContext);
1539 switch (pFirstMsg->mType)
1540 {
1541 case HOST_MSG_EXEC_CMD:
1542 HGCMSvcSetU32(&aReplyParams[1], 0); /* pid */
1543 HGCMSvcSetU32(&aReplyParams[2], PROC_STS_ERROR); /* status */
1544 HGCMSvcSetU32(&aReplyParams[3], rcSkip); /* flags / whatever */
1545 HGCMSvcSetPv(&aReplyParams[4], NULL, 0); /* data buffer */
1546 hostCallback(GUEST_MSG_EXEC_STATUS, 5, aReplyParams);
1547 break;
1548
1549 case HOST_MSG_SESSION_CREATE:
1550 HGCMSvcSetU32(&aReplyParams[1], GUEST_SESSION_NOTIFYTYPE_ERROR); /* type */
1551 HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* result */
1552 hostCallback(GUEST_MSG_SESSION_NOTIFY, 3, aReplyParams);
1553 break;
1554
1555 case HOST_MSG_EXEC_SET_INPUT:
1556 HGCMSvcSetU32(&aReplyParams[1], pFirstMsg->mParmCount >= 2 ? pFirstMsg->mpParms[1].u.uint32 : 0);
1557 HGCMSvcSetU32(&aReplyParams[2], INPUT_STS_ERROR); /* status */
1558 HGCMSvcSetU32(&aReplyParams[3], rcSkip); /* flags / whatever */
1559 HGCMSvcSetU32(&aReplyParams[4], 0); /* bytes consumed */
1560 hostCallback(GUEST_MSG_EXEC_INPUT_STATUS, 5, aReplyParams);
1561 break;
1562
1563 case HOST_MSG_FILE_OPEN:
1564 HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_OPEN); /* type*/
1565 HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */
1566 HGCMSvcSetU32(&aReplyParams[3], VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pFirstMsg->m_idContext)); /* handle */
1567 hostCallback(GUEST_MSG_FILE_NOTIFY, 4, aReplyParams);
1568 break;
1569 case HOST_MSG_FILE_CLOSE:
1570 HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_ERROR); /* type*/
1571 HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */
1572 hostCallback(GUEST_MSG_FILE_NOTIFY, 3, aReplyParams);
1573 break;
1574 case HOST_MSG_FILE_READ:
1575 case HOST_MSG_FILE_READ_AT:
1576 HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_READ); /* type */
1577 HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */
1578 HGCMSvcSetPv(&aReplyParams[3], NULL, 0); /* data buffer */
1579 hostCallback(GUEST_MSG_FILE_NOTIFY, 4, aReplyParams);
1580 break;
1581 case HOST_MSG_FILE_WRITE:
1582 case HOST_MSG_FILE_WRITE_AT:
1583 HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_WRITE); /* type */
1584 HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */
1585 HGCMSvcSetU32(&aReplyParams[3], 0); /* bytes written */
1586 hostCallback(GUEST_MSG_FILE_NOTIFY, 4, aReplyParams);
1587 break;
1588 case HOST_MSG_FILE_SEEK:
1589 HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_SEEK); /* type */
1590 HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */
1591 HGCMSvcSetU64(&aReplyParams[3], 0); /* actual */
1592 hostCallback(GUEST_MSG_FILE_NOTIFY, 4, aReplyParams);
1593 break;
1594 case HOST_MSG_FILE_TELL:
1595 HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_TELL); /* type */
1596 HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */
1597 HGCMSvcSetU64(&aReplyParams[3], 0); /* actual */
1598 hostCallback(GUEST_MSG_FILE_NOTIFY, 4, aReplyParams);
1599 break;
1600 case HOST_MSG_FILE_SET_SIZE:
1601 HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_SET_SIZE); /* type */
1602 HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */
1603 HGCMSvcSetU64(&aReplyParams[3], 0); /* actual */
1604 hostCallback(GUEST_MSG_FILE_NOTIFY, 4, aReplyParams);
1605 break;
1606
1607 case HOST_MSG_EXEC_GET_OUTPUT: /** @todo This can't be right/work. */
1608 case HOST_MSG_EXEC_TERMINATE: /** @todo This can't be right/work. */
1609 case HOST_MSG_EXEC_WAIT_FOR: /** @todo This can't be right/work. */
1610 case HOST_MSG_PATH_USER_DOCUMENTS:
1611 case HOST_MSG_PATH_USER_HOME:
1612 case HOST_MSG_PATH_RENAME:
1613 case HOST_MSG_DIR_REMOVE:
1614 default:
1615 HGCMSvcSetU32(&aReplyParams[1], pFirstMsg->mType);
1616 HGCMSvcSetU32(&aReplyParams[2], (uint32_t)rcSkip);
1617 HGCMSvcSetPv(&aReplyParams[3], NULL, 0);
1618 hostCallback(GUEST_MSG_REPLY, 4, aReplyParams);
1619 break;
1620 }
1621
1622 /*
1623 * Free the message.
1624 */
1625 pFirstMsg->Delete();
1626 }
1627 else
1628 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
1629 return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
1630 }
1631 LogFunc(("Warning: GUEST_MSG_SKIP mismatch! Found %u, caller expected %u!\n", pFirstMsg->mType, idMsg));
1632 return VERR_MISMATCH;
1633 }
1634 return VERR_NOT_FOUND;
1635}
1636
1637
1638/**
1639 * Implements GUEST_SESSION_PREPARE.
1640 *
1641 * @returns VBox status code.
1642 * @retval VINF_HGCM_ASYNC_EXECUTE on success as we complete the message.
1643 * @retval VERR_OUT_OF_RESOURCES if too many pending sessions hanging around.
1644 * @retval VERR_OUT_OF_RANGE if the session ID outside the allowed range.
1645 * @retval VERR_BUFFER_OVERFLOW if key too large.
1646 * @retval VERR_BUFFER_UNDERFLOW if key too small.
1647 * @retval VERR_ACCESS_DENIED if not master or in legacy mode.
1648 * @retval VERR_DUPLICATE if the session ID has been prepared already.
1649 *
1650 * @param pClient The client state.
1651 * @param hCall The call handle for completing it.
1652 * @param cParms Number of parameters.
1653 * @param paParms The parameters.
1654 */
1655int GstCtrlService::clientSessionPrepare(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1656{
1657 /*
1658 * Validate parameters.
1659 */
1660 ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
1661 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1662 uint32_t const idSession = paParms[0].u.uint32;
1663 ASSERT_GUEST_RETURN(idSession >= 1, VERR_OUT_OF_RANGE);
1664 ASSERT_GUEST_RETURN(idSession <= 0xfff0, VERR_OUT_OF_RANGE);
1665
1666 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_PTR, VERR_WRONG_PARAMETER_TYPE);
1667 uint32_t const cbKey = paParms[1].u.pointer.size;
1668 void const *pvKey = paParms[1].u.pointer.addr;
1669 ASSERT_GUEST_RETURN(cbKey >= 64, VERR_BUFFER_UNDERFLOW);
1670 ASSERT_GUEST_RETURN(cbKey <= _16K, VERR_BUFFER_OVERFLOW);
1671
1672 ASSERT_GUEST_RETURN(pClient->m_fIsMaster, VERR_ACCESS_DENIED);
1673 ASSERT_GUEST_RETURN(!m_fLegacyMode, VERR_ACCESS_DENIED);
1674 Assert(m_idMasterClient == pClient->m_idClient);
1675 Assert(m_pMasterClient == pClient);
1676
1677 /* Now that we know it's the master, we can check for session ID duplicates. */
1678 GstCtrlPreparedSession *pCur;
1679 RTListForEach(&m_PreparedSessions, pCur, GstCtrlPreparedSession, ListEntry)
1680 {
1681 ASSERT_GUEST_RETURN(pCur->idSession != idSession, VERR_DUPLICATE);
1682 }
1683
1684 /*
1685 * Make a copy of the session ID and key.
1686 */
1687 ASSERT_GUEST_RETURN(m_cPreparedSessions < 128, VERR_OUT_OF_RESOURCES);
1688
1689 GstCtrlPreparedSession *pPrepped = (GstCtrlPreparedSession *)RTMemAlloc(RT_UOFFSETOF_DYN(GstCtrlPreparedSession, abKey[cbKey]));
1690 AssertReturn(pPrepped, VERR_NO_MEMORY);
1691 pPrepped->idSession = idSession;
1692 pPrepped->cbKey = cbKey;
1693 memcpy(pPrepped->abKey, pvKey, cbKey);
1694
1695 RTListAppend(&m_PreparedSessions, &pPrepped->ListEntry);
1696 m_cPreparedSessions++;
1697
1698 /*
1699 * Try complete the message.
1700 */
1701 int rc = mpHelpers->pfnCallComplete(hCall, VINF_SUCCESS);
1702 if (RT_SUCCESS(rc))
1703 LogFlow(("Prepared %u with a %#x byte key (%u pending).\n", idSession, cbKey, m_cPreparedSessions));
1704 else
1705 {
1706 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
1707 RTListNodeRemove(&pPrepped->ListEntry);
1708 RTMemFree(pPrepped);
1709 m_cPreparedSessions--;
1710 }
1711 return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
1712}
1713
1714
1715/**
1716 * Implements GUEST_SESSION_CANCEL_PREPARED.
1717 *
1718 * @returns VBox status code.
1719 * @retval VINF_HGCM_ASYNC_EXECUTE on success as we complete the message.
1720 * @retval VWRN_NOT_FOUND if no session with the specified ID.
1721 * @retval VERR_ACCESS_DENIED if not master or in legacy mode.
1722 *
1723 * @param pClient The client state.
1724 * @param cParms Number of parameters.
1725 * @param paParms The parameters.
1726 */
1727int GstCtrlService::clientSessionCancelPrepared(ClientState *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1728{
1729 /*
1730 * Validate parameters.
1731 */
1732 ASSERT_GUEST_RETURN(cParms == 1, VERR_WRONG_PARAMETER_COUNT);
1733 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1734 uint32_t const idSession = paParms[0].u.uint32;
1735
1736 ASSERT_GUEST_RETURN(pClient->m_fIsMaster, VERR_ACCESS_DENIED);
1737 ASSERT_GUEST_RETURN(!m_fLegacyMode, VERR_ACCESS_DENIED);
1738 Assert(m_idMasterClient == pClient->m_idClient);
1739 Assert(m_pMasterClient == pClient);
1740
1741 /*
1742 * Do the work.
1743 */
1744 int rc = VWRN_NOT_FOUND;
1745 if (idSession == UINT32_MAX)
1746 {
1747 GstCtrlPreparedSession *pCur, *pNext;
1748 RTListForEachSafe(&m_PreparedSessions, pCur, pNext, GstCtrlPreparedSession, ListEntry)
1749 {
1750 RTListNodeRemove(&pCur->ListEntry);
1751 RTMemFree(pCur);
1752 rc = VINF_SUCCESS;
1753 }
1754 m_cPreparedSessions = 0;
1755 }
1756 else
1757 {
1758 GstCtrlPreparedSession *pCur, *pNext;
1759 RTListForEachSafe(&m_PreparedSessions, pCur, pNext, GstCtrlPreparedSession, ListEntry)
1760 {
1761 if (pCur->idSession == idSession)
1762 {
1763 RTListNodeRemove(&pCur->ListEntry);
1764 RTMemFree(pCur);
1765 m_cPreparedSessions -= 1;
1766 rc = VINF_SUCCESS;
1767 break;
1768 }
1769 }
1770 }
1771 return VINF_SUCCESS;
1772}
1773
1774
1775/**
1776 * Implements GUEST_SESSION_ACCEPT.
1777 *
1778 * @returns VBox status code.
1779 * @retval VINF_HGCM_ASYNC_EXECUTE on success as we complete the message.
1780 * @retval VERR_NOT_FOUND if the specified session ID wasn't found.
1781 * @retval VERR_MISMATCH if the key didn't match.
1782 * @retval VERR_ACCESS_DENIED if we're in legacy mode or is master.
1783 * @retval VERR_RESOURCE_BUSY if the client is already associated with a
1784 * session.
1785 *
1786 * @param pClient The client state.
1787 * @param hCall The call handle for completing it.
1788 * @param cParms Number of parameters.
1789 * @param paParms The parameters.
1790 */
1791int GstCtrlService::clientSessionAccept(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1792{
1793 /*
1794 * Validate parameters.
1795 */
1796 ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
1797 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1798 uint32_t const idSession = paParms[0].u.uint32;
1799 ASSERT_GUEST_RETURN(idSession >= 1, VERR_OUT_OF_RANGE);
1800 ASSERT_GUEST_RETURN(idSession <= 0xfff0, VERR_OUT_OF_RANGE);
1801
1802 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_PTR, VERR_WRONG_PARAMETER_TYPE);
1803 uint32_t const cbKey = paParms[1].u.pointer.size;
1804 void const *pvKey = paParms[1].u.pointer.addr;
1805 ASSERT_GUEST_RETURN(cbKey >= 64, VERR_BUFFER_UNDERFLOW);
1806 ASSERT_GUEST_RETURN(cbKey <= _16K, VERR_BUFFER_OVERFLOW);
1807
1808 ASSERT_GUEST_RETURN(!pClient->m_fIsMaster, VERR_ACCESS_DENIED);
1809 ASSERT_GUEST_RETURN(!m_fLegacyMode, VERR_ACCESS_DENIED);
1810 Assert(m_idMasterClient != pClient->m_idClient);
1811 Assert(m_pMasterClient != pClient);
1812 ASSERT_GUEST_RETURN(pClient->m_idSession == UINT32_MAX, VERR_RESOURCE_BUSY);
1813
1814 /*
1815 * Look for the specified session and match the key to it.
1816 */
1817 GstCtrlPreparedSession *pCur;
1818 RTListForEach(&m_PreparedSessions, pCur, GstCtrlPreparedSession, ListEntry)
1819 {
1820 if (pCur->idSession == idSession)
1821 {
1822 if ( pCur->cbKey == cbKey
1823 && memcmp(pCur->abKey, pvKey, cbKey) == 0)
1824 {
1825 /*
1826 * We've got a match.
1827 * Try insert it into the sessio ID map and complete the request.
1828 */
1829 try
1830 {
1831 m_SessionIdMap[idSession] = pClient;
1832 }
1833 catch (std::bad_alloc &)
1834 {
1835 LogFunc(("Out of memory!\n"));
1836 return VERR_NO_MEMORY;
1837 }
1838
1839 int rc = mpHelpers->pfnCallComplete(hCall, VINF_SUCCESS);
1840 if (RT_SUCCESS(rc))
1841 {
1842 pClient->m_idSession = idSession;
1843
1844 RTListNodeRemove(&pCur->ListEntry);
1845 RTMemFree(pCur);
1846 m_cPreparedSessions -= 1;
1847 Log(("[Client %RU32] accepted session id %u.\n", pClient->m_idClient, idSession));
1848 }
1849 else
1850 {
1851 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
1852 m_SessionIdMap.erase(idSession);
1853 }
1854 return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
1855 }
1856 LogFunc(("Key mismatch for %u!\n", pClient->m_idClient));
1857 return VERR_MISMATCH;
1858 }
1859 }
1860
1861 LogFunc(("No client prepared for %u!\n", pClient->m_idClient));
1862 return VERR_NOT_FOUND;
1863}
1864
1865
1866/**
1867 * Client asks another client (guest) session to close.
1868 *
1869 * @returns VBox status code.
1870 * @param pClient The client state.
1871 * @param cParms Number of parameters.
1872 * @param paParms Array of parameters.
1873 */
1874int GstCtrlService::clientSessionCloseOther(ClientState *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1875{
1876 /*
1877 * Validate input.
1878 */
1879 ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
1880 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1881 uint32_t const idContext = paParms[0].u.uint32;
1882
1883 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1884 uint32_t const fFlags = paParms[1].u.uint32;
1885
1886 ASSERT_GUEST_RETURN(pClient->m_fIsMaster || (m_fLegacyMode && pClient->m_idSession == UINT32_MAX), VERR_ACCESS_DENIED);
1887
1888 /*
1889 * Forward the message to the destiation.
1890 * Since we modify the first parameter, we must make a copy of the parameters.
1891 */
1892 VBOXHGCMSVCPARM aParms[2];
1893 HGCMSvcSetU64(&aParms[0], idContext | VBOX_GUESTCTRL_DST_SESSION);
1894 HGCMSvcSetU32(&aParms[1], fFlags);
1895 int rc = hostProcessMessage(HOST_MSG_SESSION_CLOSE, RT_ELEMENTS(aParms), aParms);
1896
1897 LogFlowFunc(("Closing guest context ID=%RU32 (from client ID=%RU32) returned with rc=%Rrc\n", idContext, pClient->m_idClient, rc));
1898 return rc;
1899}
1900
1901
1902/**
1903 * For compatiblity with old additions only - filtering / set session ID.
1904 *
1905 * @return VBox status code.
1906 * @param pClient The client state.
1907 * @param cParms Number of parameters.
1908 * @param paParms Array of parameters.
1909 */
1910int GstCtrlService::clientMsgOldFilterSet(ClientState *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1911{
1912 /*
1913 * Validate input and access.
1914 */
1915 ASSERT_GUEST_RETURN(cParms == 4, VERR_WRONG_PARAMETER_COUNT);
1916 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1917 uint32_t uValue = paParms[0].u.uint32;
1918 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1919 uint32_t fMaskAdd = paParms[1].u.uint32;
1920 ASSERT_GUEST_RETURN(paParms[2].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1921 uint32_t fMaskRemove = paParms[2].u.uint32;
1922 ASSERT_GUEST_RETURN(paParms[3].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /* flags, unused */
1923
1924 /*
1925 * We have a bunch of expectations here:
1926 * - Never called in non-legacy mode.
1927 * - Only called once per session.
1928 * - Never called by the master session.
1929 * - Clients that doesn't wish for any messages passes all zeros.
1930 * - All other calls has a unique session ID.
1931 */
1932 ASSERT_GUEST_LOGREL_RETURN(m_fLegacyMode, VERR_WRONG_ORDER);
1933 ASSERT_GUEST_LOGREL_MSG_RETURN(pClient->m_idSession == UINT32_MAX, ("m_idSession=%#x\n", pClient->m_idSession),
1934 VERR_WRONG_ORDER);
1935 ASSERT_GUEST_LOGREL_RETURN(!pClient->m_fIsMaster, VERR_WRONG_ORDER);
1936
1937 if (uValue == 0)
1938 {
1939 ASSERT_GUEST_LOGREL(fMaskAdd == 0);
1940 ASSERT_GUEST_LOGREL(fMaskRemove == 0);
1941 /* Nothing to do, already muted (UINT32_MAX). */
1942 }
1943 else
1944 {
1945 ASSERT_GUEST_LOGREL(fMaskAdd == UINT32_C(0xf8000000));
1946 ASSERT_GUEST_LOGREL(fMaskRemove == 0);
1947
1948 uint32_t idSession = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uValue);
1949 ASSERT_GUEST_LOGREL_MSG_RETURN(idSession > 0, ("idSession=%u (%#x)\n", idSession, uValue), VERR_OUT_OF_RANGE);
1950
1951 ClientStateMap::iterator ItConflict = m_SessionIdMap.find(idSession);
1952 ASSERT_GUEST_LOGREL_MSG_RETURN(ItConflict == m_SessionIdMap.end(),
1953 ("idSession=%u uValue=%#x idClient=%u; conflicting with client %u\n",
1954 idSession, uValue, pClient->m_idClient, ItConflict->second->m_idClient),
1955 VERR_DUPLICATE);
1956
1957 /* Commit it. */
1958 try
1959 {
1960 m_SessionIdMap[idSession] = pClient;
1961 }
1962 catch (std::bad_alloc &)
1963 {
1964 LogFunc(("Out of memory\n"));
1965 return VERR_NO_MEMORY;
1966 }
1967 pClient->m_idSession = idSession;
1968 }
1969 return VINF_SUCCESS;
1970}
1971
1972
1973/**
1974 * For compatibility with old additions only - skip the current message w/o
1975 * calling main code.
1976 *
1977 * Please note that we don't care if the caller cancelled the request, because
1978 * old additions code didn't give damn about VERR_INTERRUPT.
1979 *
1980 * @return VBox status code.
1981 * @param pClient The client state.
1982 * @param hCall The call handle for completing it.
1983 * @param cParms Number of parameters.
1984 */
1985int GstCtrlService::clientMsgOldSkip(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms)
1986{
1987 /*
1988 * Validate input and access.
1989 */
1990 ASSERT_GUEST_RETURN(cParms == 1, VERR_WRONG_PARAMETER_COUNT);
1991
1992 /*
1993 * Execute the request.
1994 *
1995 * Note! As it turns out the old and new skip should be mostly the same. The
1996 * pre-6.0 GAs (up to BETA3) has a hack which tries to issue a
1997 * VERR_NOT_SUPPORTED reply to unknown host requests, however the 5.2.x
1998 * and earlier GAs doesn't. We need old skip behavior only for the 6.0
1999 * beta GAs, nothing else.
2000 * So, we have to track whether they issued a MSG_REPLY or not. Wonderful.
2001 */
2002 HostMsg *pFirstMsg = RTListGetFirstCpp(&pClient->m_HostMsgList, HostMsg, m_ListEntry);
2003 if (pFirstMsg)
2004 {
2005 uint32_t const idMsg = pFirstMsg->mType;
2006 bool const f60BetaHackInPlay = pFirstMsg->m_f60BetaHackInPlay;
2007 int rc;
2008 if (!f60BetaHackInPlay)
2009 rc = clientMsgSkip(pClient, hCall, 0, NULL);
2010 else
2011 {
2012 RTListNodeRemove(&pFirstMsg->m_ListEntry);
2013 pFirstMsg->Delete();
2014 rc = VINF_SUCCESS;
2015 }
2016
2017 /* Reset legacy message wait/get state: */
2018 if (RT_SUCCESS(rc))
2019 {
2020 pClient->mHostMsgRc = VINF_SUCCESS;
2021 pClient->mHostMsgTries = 0;
2022 pClient->mPeekCount = 0;
2023 }
2024
2025 LogFlowFunc(("[Client %RU32] Legacy message skipping: Skipped %u (%s)%s!\n",
2026 pClient->m_idClient, idMsg, GstCtrlHostMsgtoStr((eHostMsg)idMsg), f60BetaHackInPlay ? " hack style" : ""));
2027 NOREF(idMsg);
2028 return rc;
2029 }
2030 LogFlowFunc(("[Client %RU32] Legacy message skipping: No messages pending!\n", pClient->m_idClient));
2031 return VINF_SUCCESS;
2032}
2033
2034
2035/**
2036 * Forwards client call to the Main API.
2037 *
2038 * This is typically notifications and replys.
2039 *
2040 * @returns VBox status code.
2041 * @param pClient The client state.
2042 * @param idMsg Message ID that occured.
2043 * @param cParms Number of parameters.
2044 * @param paParms Array of parameters.
2045 */
2046int GstCtrlService::clientToMain(ClientState *pClient, uint32_t idMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
2047{
2048 /*
2049 * Do input validation. This class of messages all have a 32-bit context ID as
2050 * the first parameter, so make sure it is there and appropriate for the caller.
2051 */
2052 ASSERT_GUEST_RETURN(cParms >= 1, VERR_WRONG_PARAMETER_COUNT);
2053 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_COUNT);
2054 uint32_t const idContext = paParms[0].u.uint32;
2055 uint32_t const idSession = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(idContext);
2056
2057 ASSERT_GUEST_MSG_RETURN( pClient->m_idSession == idSession
2058 || pClient->m_fIsMaster
2059 || ( m_fLegacyMode /* (see bugref:9313#c16) */
2060 && pClient->m_idSession == UINT32_MAX
2061 && ( idMsg == GUEST_MSG_EXEC_STATUS
2062 || idMsg == GUEST_MSG_SESSION_NOTIFY)),
2063 ("idSession=%u (CID=%#x) m_idSession=%u idClient=%u idMsg=%u (%s)\n", idSession, idContext,
2064 pClient->m_idSession, pClient->m_idClient, idMsg, GstCtrlGuestMsgToStr((eGuestMsg)idMsg)),
2065 VERR_ACCESS_DENIED);
2066
2067 /*
2068 * It seems okay, so make the call.
2069 */
2070 return hostCallback(idMsg, cParms, paParms);
2071}
2072
2073
2074/**
2075 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnCall}
2076 *
2077 * @note All functions which do not involve an unreasonable delay will be
2078 * handled synchronously. If needed, we will add a request handler
2079 * thread in future for those which do.
2080 * @thread HGCM
2081 */
2082/*static*/ DECLCALLBACK(void)
2083GstCtrlService::svcCall(void *pvService, VBOXHGCMCALLHANDLE hCall, uint32_t idClient, void *pvClient,
2084 uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[], uint64_t tsArrival)
2085{
2086 LogFlowFunc(("[Client %RU32] u32Function=%RU32 (%s), cParms=%RU32, paParms=0x%p\n",
2087 idClient, u32Function, GstCtrlGuestMsgToStr((eGuestMsg)u32Function), cParms, paParms));
2088 RT_NOREF(tsArrival, idClient);
2089
2090 /*
2091 * Convert opaque pointers to typed ones.
2092 */
2093 SELF *pThis = reinterpret_cast<SELF *>(pvService);
2094 AssertReturnVoidStmt(pThis, pThis->mpHelpers->pfnCallComplete(hCall, VERR_INTERNAL_ERROR_5));
2095 ClientState *pClient = reinterpret_cast<ClientState *>(pvClient);
2096 AssertReturnVoidStmt(pClient, pThis->mpHelpers->pfnCallComplete(hCall, VERR_INVALID_CLIENT_ID));
2097 Assert(pClient->m_idClient == idClient);
2098
2099 /*
2100 * Do the dispatching.
2101 */
2102 int rc;
2103 switch (u32Function)
2104 {
2105 case GUEST_MSG_MAKE_ME_MASTER:
2106 LogFlowFunc(("[Client %RU32] GUEST_MAKE_ME_MASTER\n", idClient));
2107 rc = pThis->clientMakeMeMaster(pClient, hCall, cParms);
2108 break;
2109 case GUEST_MSG_REPORT_FEATURES:
2110 LogFlowFunc(("[Client %RU32] GUEST_MSG_REPORT_FEATURES\n", idClient));
2111 rc = pThis->clientReportFeatures(pClient, hCall, cParms, paParms);
2112 break;
2113 case GUEST_MSG_QUERY_FEATURES:
2114 LogFlowFunc(("[Client %RU32] GUEST_MSG_QUERY_FEATURES\n", idClient));
2115 rc = pThis->clientQueryFeatures(hCall, cParms, paParms);
2116 break;
2117 case GUEST_MSG_PEEK_NOWAIT:
2118 LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_NOWAIT\n", idClient));
2119 rc = pThis->clientMsgPeek(pClient, hCall, cParms, paParms, false /*fWait*/);
2120 break;
2121 case GUEST_MSG_PEEK_WAIT:
2122 LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_WAIT\n", idClient));
2123 rc = pThis->clientMsgPeek(pClient, hCall, cParms, paParms, true /*fWait*/);
2124 break;
2125 case GUEST_MSG_GET:
2126 LogFlowFunc(("[Client %RU32] GUEST_MSG_GET\n", idClient));
2127 rc = pThis->clientMsgGet(pClient, hCall, cParms, paParms);
2128 break;
2129 case GUEST_MSG_CANCEL:
2130 LogFlowFunc(("[Client %RU32] GUEST_MSG_CANCEL\n", idClient));
2131 rc = pThis->clientMsgCancel(pClient, cParms);
2132 break;
2133 case GUEST_MSG_SKIP:
2134 LogFlowFunc(("[Client %RU32] GUEST_MSG_SKIP\n", idClient));
2135 rc = pThis->clientMsgSkip(pClient, hCall, cParms, paParms);
2136 break;
2137 case GUEST_MSG_SESSION_PREPARE:
2138 LogFlowFunc(("[Client %RU32] GUEST_SESSION_PREPARE\n", idClient));
2139 rc = pThis->clientSessionPrepare(pClient, hCall, cParms, paParms);
2140 break;
2141 case GUEST_MSG_SESSION_CANCEL_PREPARED:
2142 LogFlowFunc(("[Client %RU32] GUEST_SESSION_CANCEL_PREPARED\n", idClient));
2143 rc = pThis->clientSessionCancelPrepared(pClient, cParms, paParms);
2144 break;
2145 case GUEST_MSG_SESSION_ACCEPT:
2146 LogFlowFunc(("[Client %RU32] GUEST_SESSION_ACCEPT\n", idClient));
2147 rc = pThis->clientSessionAccept(pClient, hCall, cParms, paParms);
2148 break;
2149 case GUEST_MSG_SESSION_CLOSE:
2150 LogFlowFunc(("[Client %RU32] GUEST_SESSION_CLOSE\n", idClient));
2151 rc = pThis->clientSessionCloseOther(pClient, cParms, paParms);
2152 break;
2153
2154 /*
2155 * Stuff the goes to various main objects:
2156 */
2157 case GUEST_MSG_REPLY:
2158 if (cParms >= 3 && paParms[2].u.uint32 == (uint32_t)VERR_NOT_SUPPORTED)
2159 {
2160 HostMsg *pFirstMsg = RTListGetFirstCpp(&pClient->m_HostMsgList, HostMsg, m_ListEntry);
2161 if (pFirstMsg && pFirstMsg->m_idContext == paParms[0].u.uint32)
2162 pFirstMsg->m_f60BetaHackInPlay = true;
2163 }
2164 RT_FALL_THROUGH();
2165 case GUEST_MSG_PROGRESS_UPDATE:
2166 case GUEST_MSG_SESSION_NOTIFY:
2167 case GUEST_MSG_EXEC_OUTPUT:
2168 case GUEST_MSG_EXEC_STATUS:
2169 case GUEST_MSG_EXEC_INPUT_STATUS:
2170 case GUEST_MSG_EXEC_IO_NOTIFY:
2171 case GUEST_MSG_DIR_NOTIFY:
2172 case GUEST_MSG_FILE_NOTIFY:
2173 LogFlowFunc(("[Client %RU32] %s\n", idClient, GstCtrlGuestMsgToStr((eGuestMsg)u32Function)));
2174 rc = pThis->clientToMain(pClient, u32Function /* Msg */, cParms, paParms);
2175 Assert(rc != VINF_HGCM_ASYNC_EXECUTE);
2176 break;
2177
2178 /*
2179 * The remaining messages are here for compatibility with older Guest Additions:
2180 */
2181 case GUEST_MSG_WAIT:
2182 LogFlowFunc(("[Client %RU32] GUEST_MSG_WAIT\n", idClient));
2183 pThis->clientMsgOldGet(pClient, hCall, cParms, paParms);
2184 rc = VINF_HGCM_ASYNC_EXECUTE;
2185 break;
2186
2187 case GUEST_MSG_SKIP_OLD:
2188 LogFlowFunc(("[Client %RU32] GUEST_MSG_SKIP_OLD\n", idClient));
2189 rc = pThis->clientMsgOldSkip(pClient, hCall, cParms);
2190 break;
2191
2192 case GUEST_MSG_FILTER_SET:
2193 LogFlowFunc(("[Client %RU32] GUEST_MSG_FILTER_SET\n", idClient));
2194 rc = pThis->clientMsgOldFilterSet(pClient, cParms, paParms);
2195 break;
2196
2197 case GUEST_MSG_FILTER_UNSET:
2198 LogFlowFunc(("[Client %RU32] GUEST_MSG_FILTER_UNSET\n", idClient));
2199 rc = VERR_NOT_IMPLEMENTED;
2200 break;
2201
2202 /*
2203 * Anything else shall return invalid function.
2204 * Note! We used to return VINF_SUCCESS for these. See bugref:9313
2205 * and Guest::i_notifyCtrlDispatcher().
2206 */
2207 default:
2208 ASSERT_GUEST_MSG_FAILED(("u32Function=%RU32 (%#x)\n", u32Function, u32Function));
2209 rc = VERR_INVALID_FUNCTION;
2210 break;
2211 }
2212
2213 if (rc != VINF_HGCM_ASYNC_EXECUTE)
2214 {
2215 /* Tell the client that the call is complete (unblocks waiting). */
2216 LogFlowFunc(("[Client %RU32] Calling pfnCallComplete w/ rc=%Rrc\n", idClient, rc));
2217 AssertPtr(pThis->mpHelpers);
2218 pThis->mpHelpers->pfnCallComplete(hCall, rc);
2219 }
2220}
2221
2222
2223/**
2224 * Notifies the host (using low-level HGCM callbacks) about an event
2225 * which was sent from the client.
2226 *
2227 * @returns VBox status code.
2228 * @param u32Function Message ID that occured.
2229 * @param cParms Number of parameters.
2230 * @param paParms Array of parameters.
2231 */
2232int GstCtrlService::hostCallback(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
2233{
2234 LogFlowFunc(("u32Function=%RU32 (%s), cParms=%ld, paParms=%p\n",
2235 u32Function, GstCtrlGuestMsgToStr((eGuestMsg)u32Function), cParms, paParms));
2236
2237 int rc;
2238 if (mpfnHostCallback)
2239 {
2240 VBOXGUESTCTRLHOSTCALLBACK data = { cParms, paParms };
2241 rc = mpfnHostCallback(mpvHostData, u32Function, &data, sizeof(data));
2242 }
2243 else
2244 rc = VERR_NOT_SUPPORTED;
2245
2246 LogFlowFunc(("Returning rc=%Rrc\n", rc));
2247 return rc;
2248}
2249
2250
2251/**
2252 * Processes a message received from the host side and re-routes it to
2253 * a connect client on the guest.
2254 *
2255 * @returns VBox status code.
2256 * @param idMsg Message ID to process.
2257 * @param cParms Number of parameters.
2258 * @param paParms Array of parameters.
2259 */
2260int GstCtrlService::hostProcessMessage(uint32_t idMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
2261{
2262 /*
2263 * If no client is connected at all we don't buffer any host messages
2264 * and immediately return an error to the host. This avoids the host
2265 * waiting for a response from the guest side in case VBoxService on
2266 * the guest is not running/system is messed up somehow.
2267 */
2268 if (m_ClientStateMap.empty())
2269 {
2270 LogFlow(("GstCtrlService::hostProcessMessage: VERR_NOT_FOUND!\n"));
2271 return VERR_NOT_FOUND;
2272 }
2273
2274 /*
2275 * Create a host message for each destination.
2276 * Note! There is currently only one scenario in which we send a host
2277 * message to two recipients.
2278 */
2279 HostMsg *pHostMsg = new (std::nothrow) HostMsg();
2280 AssertReturn(pHostMsg, VERR_NO_MEMORY);
2281 int rc = pHostMsg->Init(idMsg, cParms, paParms);
2282 if (RT_SUCCESS(rc))
2283 {
2284 uint64_t const fDestinations = pHostMsg->m_idContextAndDst & VBOX_GUESTCTRL_DST_BOTH;
2285 HostMsg *pHostMsg2 = NULL;
2286 if (fDestinations != VBOX_GUESTCTRL_DST_BOTH)
2287 { /* likely */ }
2288 else
2289 {
2290 pHostMsg2 = new (std::nothrow) HostMsg();
2291 if (pHostMsg2)
2292 rc = pHostMsg2->Init(idMsg, cParms, paParms);
2293 else
2294 rc = VERR_NO_MEMORY;
2295 }
2296 if (RT_SUCCESS(rc))
2297 {
2298 LogFlowFunc(("Handling host message m_idContextAndDst=%#RX64, idMsg=%RU32, cParms=%RU32, paParms=%p, cClients=%zu\n",
2299 pHostMsg->m_idContextAndDst, idMsg, cParms, paParms, m_ClientStateMap.size()));
2300
2301 /*
2302 * Find the message destination and post it to the client. If the
2303 * session ID doesn't match any particular client it goes to the master.
2304 */
2305 AssertMsg(!m_ClientStateMap.empty(), ("Client state map is empty when it should not be!\n"));
2306
2307 /* Dispatch to the session. */
2308 if (fDestinations & VBOX_GUESTCTRL_DST_SESSION)
2309 {
2310 uint32_t const idSession = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pHostMsg->m_idContext);
2311 ClientStateMap::iterator It = m_SessionIdMap.find(idSession);
2312 if (It != m_SessionIdMap.end())
2313 {
2314 ClientState *pClient = It->second;
2315 Assert(pClient->m_idSession == idSession);
2316 RTListAppend(&pClient->m_HostMsgList, &pHostMsg->m_ListEntry);
2317 pHostMsg = pHostMsg2;
2318 pHostMsg2 = NULL;
2319
2320 int rc2 = pClient->Wakeup();
2321 LogFlowFunc(("Woke up client ID=%RU32 -> rc=%Rrc\n", pClient->m_idClient, rc2));
2322 RT_NOREF(rc2);
2323 rc = VINF_SUCCESS;
2324 }
2325 else
2326 {
2327 LogFunc(("No client with session ID %u was found! (idMsg=%d %s)\n",
2328 idSession, idMsg, GstCtrlHostMsgtoStr((eHostMsg)idMsg)));
2329 rc = !(fDestinations & VBOX_GUESTCTRL_DST_ROOT_SVC) ? VERR_NOT_FOUND : VWRN_NOT_FOUND;
2330 }
2331 }
2332
2333 /* Does the message go to the root service? */
2334 if ( (fDestinations & VBOX_GUESTCTRL_DST_ROOT_SVC)
2335 && RT_SUCCESS(rc))
2336 {
2337 Assert(pHostMsg);
2338 if (m_pMasterClient)
2339 {
2340 RTListAppend(&m_pMasterClient->m_HostMsgList, &pHostMsg->m_ListEntry);
2341 pHostMsg = NULL;
2342
2343 int rc2 = m_pMasterClient->Wakeup();
2344 LogFlowFunc(("Woke up client ID=%RU32 (master) -> rc=%Rrc\n", m_pMasterClient->m_idClient, rc2));
2345 NOREF(rc2);
2346 }
2347 else
2348 rc = VERR_NOT_FOUND;
2349 }
2350 }
2351
2352 /* Drop unset messages. */
2353 if (pHostMsg2)
2354 pHostMsg2->Delete();
2355 }
2356 if (pHostMsg)
2357 pHostMsg->Delete();
2358
2359 if (RT_FAILURE(rc))
2360 LogFunc(("Failed %Rrc (idMsg=%u, cParms=%u)\n", rc, idMsg, cParms));
2361 return rc;
2362}
2363
2364
2365/**
2366 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnHostCall,
2367 * Wraps to the hostProcessMessage() member function.}
2368 */
2369/*static*/ DECLCALLBACK(int)
2370GstCtrlService::svcHostCall(void *pvService, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
2371{
2372 AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
2373 SELF *pThis = reinterpret_cast<SELF *>(pvService);
2374 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2375
2376 LogFlowFunc(("u32Function=%RU32, cParms=%RU32, paParms=0x%p\n", u32Function, cParms, paParms));
2377 AssertReturn(u32Function != HOST_MSG_CANCEL_PENDING_WAITS, VERR_INVALID_FUNCTION);
2378 return pThis->hostProcessMessage(u32Function, cParms, paParms);
2379}
2380
2381
2382
2383
2384/**
2385 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnSaveState}
2386 */
2387/*static*/ DECLCALLBACK(int)
2388GstCtrlService::svcSaveState(void *pvService, uint32_t idClient, void *pvClient, PSSMHANDLE pSSM)
2389{
2390 RT_NOREF(pvClient);
2391 SELF *pThis = reinterpret_cast<SELF *>(pvService);
2392 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2393
2394 /* Note! We don't need to save the idSession here because it's only used
2395 for sessions and the sessions are not persistent across a state
2396 save/restore. The Main objects aren't there. Clients shuts down.
2397 Only the root service survives, so remember who that is and its mode. */
2398
2399 SSMR3PutU32(pSSM, 1);
2400 SSMR3PutBool(pSSM, pThis->m_fLegacyMode);
2401 return SSMR3PutBool(pSSM, idClient == pThis->m_idMasterClient);
2402}
2403
2404
2405/**
2406 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnLoadState}
2407 */
2408/*static*/ DECLCALLBACK(int)
2409GstCtrlService::svcLoadState(void *pvService, uint32_t idClient, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion)
2410{
2411 SELF *pThis = reinterpret_cast<SELF *>(pvService);
2412 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2413 ClientState *pClient = reinterpret_cast<ClientState *>(pvClient);
2414 AssertReturn(pClient, VERR_INVALID_CLIENT_ID);
2415 Assert(pClient->m_idClient == idClient);
2416
2417 if (uVersion >= HGCM_SAVED_STATE_VERSION)
2418 {
2419 uint32_t uSubVersion;
2420 int rc = SSMR3GetU32(pSSM, &uSubVersion);
2421 AssertRCReturn(rc, rc);
2422 if (uSubVersion != 1)
2423 return SSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
2424 "sub version %u, expected 1\n", uSubVersion);
2425 bool fLegacyMode;
2426 rc = SSMR3GetBool(pSSM, &fLegacyMode);
2427 AssertRCReturn(rc, rc);
2428 pThis->m_fLegacyMode = fLegacyMode;
2429
2430 bool fIsMaster;
2431 rc = SSMR3GetBool(pSSM, &fIsMaster);
2432 AssertRCReturn(rc, rc);
2433
2434 pClient->m_fIsMaster = fIsMaster;
2435 if (fIsMaster)
2436 {
2437 pThis->m_pMasterClient = pClient;
2438 pThis->m_idMasterClient = idClient;
2439 }
2440 }
2441 else
2442 {
2443 /*
2444 * For old saved states we have to guess at who should be the master.
2445 * Given how HGCMService::CreateAndConnectClient and associates manage
2446 * and saves the client, the first client connecting will be restored
2447 * first. The only time this might go wrong if the there are zombie
2448 * VBoxService session processes in the restored guest, and I don't
2449 * we need to care too much about that scenario.
2450 *
2451 * Given how HGCM first re-connects the clients before this function
2452 * gets called, there isn't anything we need to do here it turns out. :-)
2453 */
2454 }
2455 pClient->m_fRestored = true;
2456 return VINF_SUCCESS;
2457}
2458
2459
2460/**
2461 * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnRegisterExtension,
2462 * Installs a host callback for notifications of property changes.}
2463 */
2464/*static*/ DECLCALLBACK(int) GstCtrlService::svcRegisterExtension(void *pvService, PFNHGCMSVCEXT pfnExtension, void *pvExtension)
2465{
2466 SELF *pThis = reinterpret_cast<SELF *>(pvService);
2467 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2468 AssertPtrNullReturn(pfnExtension, VERR_INVALID_POINTER);
2469
2470 pThis->mpfnHostCallback = pfnExtension;
2471 pThis->mpvHostData = pvExtension;
2472 return VINF_SUCCESS;
2473}
2474
2475
2476/**
2477 * @copydoc VBOXHGCMSVCLOAD
2478 */
2479extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
2480{
2481 int rc = VINF_SUCCESS;
2482
2483 LogFlowFunc(("pTable=%p\n", pTable));
2484
2485 if (!VALID_PTR(pTable))
2486 {
2487 rc = VERR_INVALID_PARAMETER;
2488 }
2489 else
2490 {
2491 LogFlowFunc(("pTable->cbSize=%d, pTable->u32Version=0x%08X\n", pTable->cbSize, pTable->u32Version));
2492
2493 if ( pTable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
2494 || pTable->u32Version != VBOX_HGCM_SVC_VERSION)
2495 {
2496 rc = VERR_VERSION_MISMATCH;
2497 }
2498 else
2499 {
2500 GstCtrlService *pService = NULL;
2501 /* No exceptions may propagate outside. */
2502 try
2503 {
2504 pService = new GstCtrlService(pTable->pHelpers);
2505 }
2506 catch (int rcThrown)
2507 {
2508 rc = rcThrown;
2509 }
2510 catch(std::bad_alloc &)
2511 {
2512 rc = VERR_NO_MEMORY;
2513 }
2514
2515 if (RT_SUCCESS(rc))
2516 {
2517 /*
2518 * We don't need an additional client data area on the host,
2519 * because we're a class which can have members for that :-).
2520 */
2521 pTable->cbClient = sizeof(ClientState);
2522
2523 /* Register functions. */
2524 pTable->pfnUnload = GstCtrlService::svcUnload;
2525 pTable->pfnConnect = GstCtrlService::svcConnect;
2526 pTable->pfnDisconnect = GstCtrlService::svcDisconnect;
2527 pTable->pfnCall = GstCtrlService::svcCall;
2528 pTable->pfnHostCall = GstCtrlService::svcHostCall;
2529 pTable->pfnSaveState = GstCtrlService::svcSaveState;
2530 pTable->pfnLoadState = GstCtrlService::svcLoadState;
2531 pTable->pfnRegisterExtension = GstCtrlService::svcRegisterExtension;
2532 pTable->pfnNotify = NULL;
2533
2534 /* Service specific initialization. */
2535 pTable->pvService = pService;
2536 }
2537 else
2538 {
2539 if (pService)
2540 {
2541 delete pService;
2542 pService = NULL;
2543 }
2544 }
2545 }
2546 }
2547
2548 LogFlowFunc(("Returning %Rrc\n", rc));
2549 return rc;
2550}
2551
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