VirtualBox

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

Last change on this file since 98526 was 98526, checked in by vboxsync, 22 months ago

Guest Control: Initial commit (work in progress, disabled by default). bugref:9783

IGuestDirectory:

Added new attributes id + status + an own event source. Also added for rewind support via rewind().

New event types for guest directory [un]registration, state changes and entry reads.

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