VirtualBox

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

最後變更 在這個檔案從76955是 76955,由 vboxsync 提交於 6 年 前

Guest Control/service: A bit of try/catch cleanup.

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

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