VirtualBox

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

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

GuestControl: Use a map for the session IDs to optimize GstCtrlService::hostProcessCommand a little. bugref:9313

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