VirtualBox

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

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

HGCM,GuestProps: Added HGCM service notifications for VM power-on, VM resume, VM suspend, VM reset and VM power-off. Made use of the first two in guest properties to wake up guest programs waiting on any of the host version properties. Also moved inserting sysprep and host version properties to the services as that's a better home than ConsoleImpl/VMMDevInterface in my opinion.

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