VirtualBox

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

Last change on this file since 106199 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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