VirtualBox

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

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

GuestControl: Made GUEST_MSG_SKIP_OLD work better for 5.2.x GAs. Worked around a couple of assertions in legacy mode. bugref:9313

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