VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp@ 81790

Last change on this file since 81790 was 81769, checked in by vboxsync, 5 years ago

Shared Clipboard: Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 81.3 KB
Line 
1/* $Id: VBoxSharedClipboardSvc.cpp 81769 2019-11-11 16:40:27Z vboxsync $ */
2/** @file
3 * Shared Clipboard Service - Host service entry points.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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
19/** @page pg_hostclip The Shared Clipboard Host Service
20 *
21 * The shared clipboard host service is the host half of the clibpoard proxying
22 * between the host and the guest. The guest parts live in VBoxClient, VBoxTray
23 * and VBoxService depending on the OS, with code shared between host and guest
24 * under src/VBox/GuestHost/SharedClipboard/.
25 *
26 * The service is split into a platform-independent core and platform-specific
27 * backends. The service defines two communication protocols - one to
28 * communicate with the clipboard service running on the guest, and one to
29 * communicate with the backend. These will be described in a very skeletal
30 * fashion here.
31 *
32 * r=bird: The "two communication protocols" does not seems to be factual, there
33 * is only one protocol, the first one mentioned. It cannot be backend
34 * specific, because the guest/host protocol is platform and backend agnostic in
35 * nature. You may call it versions, but I take a great dislike to "protocol
36 * versions" here, as you've just extended the existing protocol with a feature
37 * that allows to transfer files and directories too. See @bugref{9437#c39}.
38 *
39 *
40 * @section sec_hostclip_guest_proto The guest communication protocol
41 *
42 * The guest clipboard service communicates with the host service over HGCM
43 * (the host is a HGCM service). HGCM is connection based, so the guest side
44 * has to connect before anything else can be done. (Windows hosts currently
45 * only support one simultaneous connection.) Once it has connected, it can
46 * send messages to the host services, some of which will receive immediate
47 * replies from the host, others which will block till a reply becomes
48 * available. The latter is because HGCM does't allow the host to initiate
49 * communication, it must be guest triggered. The HGCM service is single
50 * threaded, so it doesn't matter if the guest tries to send lots of requests in
51 * parallel, the service will process them one at the time.
52 *
53 * There are currently four messages defined. The first is
54 * VBOX_SHCL_GUEST_FN_MSG_GET / VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD, which waits
55 * for a message from the host. If a host message is sent while the guest is
56 * not waiting, it will be queued until the guest requests it. The host code
57 * only supports a single simultaneous GET call from one client guest.
58 *
59 * The second guest message is VBOX_SHCL_GUEST_FN_FORMATS_REPORT, which tells
60 * the host that the guest has new clipboard data available. The third is
61 * VBOX_SHCL_GUEST_FN_DATA_READ, which asks the host to send its clipboard data
62 * and waits until it arrives. The host supports at most one simultaneous
63 * VBOX_SHCL_GUEST_FN_DATA_READ call from a guest - if a second call is made
64 * before the first has returned, the first will be aborted.
65 *
66 * The last guest message is VBOX_SHCL_GUEST_FN_DATA_WRITE, which is used to
67 * send the contents of the guest clipboard to the host. This call should be
68 * used after the host has requested data from the guest.
69 *
70 *
71 * @section sec_hostclip_backend_proto The communication protocol with the
72 * platform-specific backend
73 *
74 * The initial protocol implementation (called protocol v0) was very simple,
75 * and could only handle simple data (like copied text and so on). It also
76 * was limited to two (2) fixed parameters at all times.
77 *
78 * Since VBox 6.1 a newer protocol (v1) has been established to also support
79 * file transfers. This protocol uses a (per-client) message queue instead
80 * (see VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD vs. VBOX_SHCL_GUEST_FN_GET_HOST_MSG).
81 *
82 * To distinguish the old (legacy) or new(er) protocol, the VBOX_SHCL_GUEST_FN_CONNECT
83 * message has been introduced. If an older guest does not send this message,
84 * an appropriate translation will be done to serve older Guest Additions (< 6.1).
85 *
86 * The protocol also support out-of-order messages by using so-called "context IDs",
87 * which are generated by the host. A context ID consists of a so-called "source event ID"
88 * and a so-called "event ID". Each HGCM client has an own, random, source event ID and
89 * generates non-deterministic event IDs so that the guest side does not known what
90 * comes next; the guest side has to reply with the same conext ID which was sent by
91 * the host request.
92 *
93 * Also see the protocol changelog at VBoxShClSvc.h.
94 *
95 *
96 * @section sec_uri_intro Transferring files
97 *
98 * Since VBox x.x.x transferring files via Shared Clipboard is supported.
99 * See the VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS define for supported / enabled
100 * platforms. This is called "Shared Clipboard transfers".
101 *
102 * Copying files / directories from guest A to guest B requires the host
103 * service to act as a proxy and cache, as we don't allow direct VM-to-VM
104 * communication. Copying from / to the host also is taken into account.
105 *
106 * At the moment a transfer is a all-or-nothing operation, e.g. it either
107 * completes or fails completely. There might be callbacks in the future
108 * to e.g. skip failing entries.
109 *
110 * Known limitations:
111 *
112 * - Support for VRDE (VRDP) is not implemented yet (see #9498).
113 * - Unicode support on Windows hosts / guests is not enabled (yet).
114 * - Symbolic links / Windows junctions are not allowed.
115 * - Windows alternate data streams (ADS) are not allowed.
116 * - No support for ACLs yet.
117 * - No (maybe never) support for NT4.
118 *
119 * @section sec_transfers_areas Clipboard areas.
120 *
121 * For larger / longer transfers there might be file data
122 * temporarily cached on the host, which has not been transferred to the
123 * destination yet. Such a cache is called a "Shared Clipboard Area", which
124 * in turn is identified by a unique ID across all VMs running on the same
125 * host. To control the access (and needed cleanup) of such clipboard areas,
126 * VBoxSVC (Main) is used for this task. A Shared Clipboard client can register,
127 * unregister, attach to and detach from a clipboard area. If all references
128 * to a clipboard area are released, a clipboard area gets detroyed automatically
129 * by VBoxSVC.
130 *
131 * By default a clipboard area lives in the user's temporary directory in the
132 * sub folder "VirtualBox Shared Clipboards/clipboard-<ID>". VBoxSVC does not
133 * do any file locking in a clipboard area, but keeps the clipboard areas's
134 * directory open to prevent deletion by third party processes.
135 *
136 * @todo We might use some VFS / container (IPRT?) for this instead of the
137 * host's file system directly?
138 * bird> Yes, but may take some work as we don't have the pick and choose
139 * kind of VFS containers implemented yet.
140 *
141 * @section sec_transfer_structure Transfer handling structure
142 *
143 * All structures / classes are designed for running on both, on the guest
144 * (via VBoxTray / VBoxClient) or on the host (host service) to avoid code
145 * duplication where applicable.
146 *
147 * Per HGCM client there is a so-called "transfer context", which in turn can
148 * have one or mulitple so-called "Shared Clipboard transfer" objects. At the
149 * moment we only support on concurrent Shared Clipboard transfer per transfer
150 * context. It's being used for reading from a source or writing to destination,
151 * depening on its direction. An Shared Clipboard transfer can have optional
152 * callbacks which might be needed by various implementations. Also, transfers
153 * optionally can run in an asynchronous thread to prevent blocking the UI while
154 * running.
155 *
156 * A Shared Clipboard transfer can maintain its own clipboard area; for the host
157 * service such a clipboard area is coupled to a clipboard area registered or
158 * attached with VBoxSVC. This is needed because multiple transfers from
159 * multiple VMs (n:n) can rely on the same clipboard area, so there needs a
160 * master keeping tracking of a clipboard area. To minimize IPC traffic only the
161 * minimum de/attaching is done at the moment. A clipboard area gets cleaned up
162 * (i.e. physically deleted) if no references are held to it anymore, or if
163 * VBoxSVC goes down.
164 *
165 * @section sec_transfer_providers Transfer providers
166 *
167 * For certain implementations (for example on Windows guests / hosts, using
168 * IDataObject and IStream objects) a more flexible approach reqarding reading /
169 * writing is needed. For this so-called transfer providers abstract the way of how
170 * data is being read / written in the current context (host / guest), while
171 * the rest of the code stays the same.
172 *
173 * @section sec_transfer_protocol Transfer protocol
174 *
175 * The host service issues commands which the guest has to respond with an own
176 * message to. The protocol itself is designed so that it has primitives to list
177 * directories and open/close/read/write file system objects.
178 *
179 * Note that this is different from the DnD approach, as Shared Clipboard transfers
180 * need to be deeper integrated within the host / guest OS (i.e. for progress UI),
181 * and this might require non-monolithic / random access APIs to achieve.
182 *
183 * As there can be multiple file system objects (fs objects) selected for transfer,
184 * a transfer can be queried for its root entries, which then contains the top-level
185 * elements. Based on these elements, (a) (recursive) listing(s) can be performed
186 * to (partially) walk down into directories and query fs object information. The
187 * provider provides appropriate interface for this, even if not all implementations
188 * might need this mechanism.
189 *
190 * An Shared Clipboard transfer has three stages:
191 * - 1. Announcement: An Shared Clipboard transfer-compatible format (currently only one format available)
192 * has been announced, the destination side creates a transfer object, which then,
193 * depending on the actual implementation, can be used to tell the OS that
194 * there is transfer (file) data available.
195 * At this point this just acts as a (kind-of) promise to the OS that we
196 * can provide (file) data at some later point in time.
197 *
198 * - 2. Initialization: As soon as the OS requests the (file) data, mostly triggered
199 * by the user starting a paste operation (CTRL + V), the transfer get initialized
200 * on the destination side, which in turn lets the source know that a transfer
201 * is going to happen.
202 *
203 * - 3. Transfer: At this stage the actual transfer from source to the destination takes
204 * place. How the actual transfer is structurized (e.g. which files / directories
205 * are transferred in which order) depends on the destination implementation. This
206 * is necessary in order to fulfill requirements on the destination side with
207 * regards to ETA calculation or other dependencies.
208 * Both sides can abort or cancel the transfer at any time.
209 */
210
211
212/*********************************************************************************************************************************
213* Header Files *
214*********************************************************************************************************************************/
215#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
216#include <VBox/log.h>
217
218#include <VBox/AssertGuest.h>
219#include <VBox/GuestHost/clipboard-helper.h>
220#include <VBox/HostServices/Service.h>
221#include <VBox/HostServices/VBoxClipboardSvc.h>
222#include <VBox/HostServices/VBoxClipboardExt.h>
223
224#include <iprt/alloc.h>
225#include <iprt/string.h>
226#include <iprt/assert.h>
227#include <iprt/critsect.h>
228#include <iprt/rand.h>
229
230#include <VBox/err.h>
231#include <VBox/vmm/ssm.h>
232
233#include "VBoxSharedClipboardSvc-internal.h"
234#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
235# include "VBoxSharedClipboardSvc-transfers.h"
236#endif
237
238using namespace HGCM;
239
240
241/*********************************************************************************************************************************
242* Global Variables *
243*********************************************************************************************************************************/
244PVBOXHGCMSVCHELPERS g_pHelpers;
245
246static RTCRITSECT g_CritSect;
247/** Global Shared Clipboard mode. */
248static uint32_t g_uMode = VBOX_SHCL_MODE_OFF;
249#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
250/** Global Shared Clipboard (file) transfer mode. */
251uint32_t g_fTransferMode = VBOX_SHCL_TRANSFER_MODE_DISABLED;
252#endif
253
254/** Is the clipboard running in headless mode? */
255static bool g_fHeadless = false;
256
257/** Holds the service extension state. */
258SHCLEXTSTATE g_ExtState = { 0 };
259
260/** Global map of all connected clients. */
261ClipboardClientMap g_mapClients;
262
263/** Global list of all clients which are queued up (deferred return) and ready
264 * to process new commands. The key is the (unique) client ID. */
265ClipboardClientQueue g_listClientsDeferred;
266
267/** Host feature mask for VBOX_SHCL_GUEST_FN_REPORT_FEATURES/VBOX_SHCL_GUEST_FN_QUERY_FEATURES. */
268static uint64_t const g_fHostFeatures0 = VBOX_SHCL_HF_NONE;
269
270
271/**
272 * Returns the current Shared Clipboard service mode.
273 *
274 * @returns Current Shared Clipboard service mode.
275 */
276uint32_t ShClSvcGetMode(void)
277{
278 return g_uMode;
279}
280
281/**
282 * Getter for headless setting. Also needed by testcase.
283 *
284 * @returns Whether service currently running in headless mode or not.
285 */
286bool ShClSvcGetHeadless(void)
287{
288 return g_fHeadless;
289}
290
291static int shClSvcModeSet(uint32_t uMode)
292{
293 int rc = VERR_NOT_SUPPORTED;
294
295 switch (uMode)
296 {
297 case VBOX_SHCL_MODE_OFF:
298 RT_FALL_THROUGH();
299 case VBOX_SHCL_MODE_HOST_TO_GUEST:
300 RT_FALL_THROUGH();
301 case VBOX_SHCL_MODE_GUEST_TO_HOST:
302 RT_FALL_THROUGH();
303 case VBOX_SHCL_MODE_BIDIRECTIONAL:
304 {
305 g_uMode = uMode;
306
307 rc = VINF_SUCCESS;
308 break;
309 }
310
311 default:
312 {
313 g_uMode = VBOX_SHCL_MODE_OFF;
314 break;
315 }
316 }
317
318 LogFlowFuncLeaveRC(rc);
319 return rc;
320}
321
322bool ShClSvcLock(void)
323{
324 return RT_SUCCESS(RTCritSectEnter(&g_CritSect));
325}
326
327void ShClSvcUnlock(void)
328{
329 int rc2 = RTCritSectLeave(&g_CritSect);
330 AssertRC(rc2);
331}
332
333/**
334 * Resets a client's state message queue.
335 *
336 * @param pClient Pointer to the client data structure to reset message queue for.
337 */
338void shclSvcMsgQueueReset(PSHCLCLIENT pClient)
339{
340 LogFlowFuncEnter();
341
342 while (!pClient->queueMsg.isEmpty())
343 {
344 RTMemFree(pClient->queueMsg.last());
345 pClient->queueMsg.removeLast();
346 }
347}
348
349/**
350 * Allocates a new clipboard message.
351 *
352 * @returns Allocated clipboard message, or NULL on failure.
353 * @param uMsg Message type of message to allocate.
354 * @param cParms Number of HGCM parameters to allocate.
355 */
356PSHCLCLIENTMSG shclSvcMsgAlloc(uint32_t uMsg, uint32_t cParms)
357{
358 PSHCLCLIENTMSG pMsg = (PSHCLCLIENTMSG)RTMemAlloc(sizeof(SHCLCLIENTMSG));
359 if (pMsg)
360 {
361 pMsg->paParms = (PVBOXHGCMSVCPARM)RTMemAllocZ(sizeof(VBOXHGCMSVCPARM) * cParms);
362 if (pMsg->paParms)
363 {
364 pMsg->cParms = cParms;
365 pMsg->uMsg = uMsg;
366
367 return pMsg;
368 }
369 }
370
371 RTMemFree(pMsg);
372 return NULL;
373}
374
375/**
376 * Frees a formerly allocated clipboard message.
377 *
378 * @param pMsg Clipboard message to free.
379 * The pointer will be invalid after calling this function.
380 */
381void shclSvcMsgFree(PSHCLCLIENTMSG pMsg)
382{
383 if (!pMsg)
384 return;
385
386 if (pMsg->paParms)
387 RTMemFree(pMsg->paParms);
388
389 RTMemFree(pMsg);
390 pMsg = NULL;
391}
392
393/**
394 * Sets the VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT and VBOX_SHCL_GUEST_FN_MSG_PEEK_NOWAIT
395 * return parameters.
396 *
397 * @param pMsg Message to set return parameters to.
398 * @param paDstParms The peek parameter vector.
399 * @param cDstParms The number of peek parameters (at least two).
400 * @remarks ASSUMES the parameters has been cleared by clientMsgPeek.
401 */
402void shclSvcMsgSetPeekReturn(PSHCLCLIENTMSG pMsg, PVBOXHGCMSVCPARM paDstParms, uint32_t cDstParms)
403{
404 Assert(cDstParms >= 2);
405 if (paDstParms[0].type == VBOX_HGCM_SVC_PARM_32BIT)
406 paDstParms[0].u.uint32 = pMsg->uMsg;
407 else
408 paDstParms[0].u.uint64 = pMsg->uMsg;
409 paDstParms[1].u.uint32 = pMsg->cParms;
410
411 uint32_t i = RT_MIN(cDstParms, pMsg->cParms + 2);
412 while (i-- > 2)
413 switch (pMsg->paParms[i - 2].type)
414 {
415 case VBOX_HGCM_SVC_PARM_32BIT: paDstParms[i].u.uint32 = ~(uint32_t)sizeof(uint32_t); break;
416 case VBOX_HGCM_SVC_PARM_64BIT: paDstParms[i].u.uint32 = ~(uint32_t)sizeof(uint64_t); break;
417 case VBOX_HGCM_SVC_PARM_PTR: paDstParms[i].u.uint32 = pMsg->paParms[i - 2].u.pointer.size; break;
418 }
419}
420
421/**
422 * Sets the VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD return parameters.
423 *
424 * This function does the necessary translation between the legacy protocol (<= VBox 6.0) and the new protocols (>= VBox 6.1),
425 * as messages are always stored as >= v1 messages in the message queue.
426 *
427 * @returns VBox status code.
428 * @param pMsg Message to set return parameters to.
429 * @param paDstParms The peek parameter vector.
430 * @param cDstParms The number of peek parameters (at least two).
431 * @param pfRemove Returns whether the message can be removed from the queue or not.
432 * This is needed for certain messages which need to stay around for more than one (guest) call.
433 */
434int shclSvcMsgSetGetHostMsgOldReturn(PSHCLCLIENTMSG pMsg, PVBOXHGCMSVCPARM paDstParms, uint32_t cDstParms,
435 bool *pfRemove)
436{
437 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
438 AssertPtrReturn(paDstParms, VERR_INVALID_POINTER);
439 AssertReturn (cDstParms >= 2, VERR_INVALID_PARAMETER);
440 AssertPtrReturn(pfRemove, VERR_INVALID_POINTER);
441
442 int rc = VINF_SUCCESS;
443
444 bool fRemove = true;
445
446 LogFlowFunc(("uMsg=%RU32 (%s), cParms=%RU32\n",
447 pMsg->uMsg, ShClHostMsgToStr(pMsg->uMsg), pMsg->cParms));
448
449 switch (pMsg->uMsg)
450 {
451 case VBOX_SHCL_HOST_MSG_QUIT:
452 {
453 HGCMSvcSetU32(&paDstParms[0], VBOX_SHCL_HOST_MSG_QUIT);
454 HGCMSvcSetU32(&paDstParms[1], 0 /* Not used */);
455 break;
456 }
457
458 case VBOX_SHCL_HOST_MSG_READ_DATA:
459 {
460 HGCMSvcSetU32(&paDstParms[0], VBOX_SHCL_HOST_MSG_READ_DATA);
461 AssertBreakStmt(pMsg->cParms >= 2, rc = VERR_INVALID_PARAMETER); /* Paranoia. */
462 uint32_t uSrcFmt = VBOX_SHCL_FMT_NONE;
463 uint32_t uDstFmt = VBOX_SHCL_FMT_NONE;
464 rc = HGCMSvcGetU32(&pMsg->paParms[2] /* uFormat */, &uSrcFmt);
465 if (RT_SUCCESS(rc))
466 {
467 if (uSrcFmt & VBOX_SHCL_FMT_UNICODETEXT)
468 uDstFmt = VBOX_SHCL_FMT_UNICODETEXT;
469 else if (uSrcFmt & VBOX_SHCL_FMT_BITMAP)
470 uDstFmt = VBOX_SHCL_FMT_BITMAP;
471 else if (uSrcFmt & VBOX_SHCL_FMT_HTML)
472 uDstFmt = VBOX_SHCL_FMT_HTML;
473 else
474 AssertStmt(uSrcFmt == VBOX_SHCL_FMT_NONE, uSrcFmt = VBOX_SHCL_FMT_NONE);
475
476 LogFlowFunc(("uSrcFmt=0x%x, uDstFmt=0x%x\n", uSrcFmt, uDstFmt));
477
478 /* Remove format we're going to return from the queued message. */
479 uSrcFmt &= ~uDstFmt;
480 HGCMSvcSetU32(&pMsg->paParms[2], uSrcFmt);
481
482 /* Only report back one format at a time. */
483 HGCMSvcSetU32(&paDstParms[1], uDstFmt);
484
485 /* If no more formats are left, the message can be removed finally. */
486 fRemove = uSrcFmt == VBOX_SHCL_FMT_NONE;
487
488 LogFlowFunc(("uSrcFmt=0x%x, fRemove=%RTbool\n", uSrcFmt, fRemove));
489 }
490 break;
491 }
492
493 case VBOX_SHCL_HOST_MSG_FORMATS_REPORT:
494 {
495 HGCMSvcSetU32(&paDstParms[0], VBOX_SHCL_HOST_MSG_FORMATS_REPORT);
496 AssertBreakStmt(pMsg->cParms >= 2, rc = VERR_INVALID_PARAMETER); /* Paranoia. */
497 uint32_t uFmts;
498 rc = HGCMSvcGetU32(&pMsg->paParms[1] /* uFormats */, &uFmts);
499 if (RT_SUCCESS(rc))
500 HGCMSvcSetU32(&paDstParms[1], uFmts);
501 break;
502 }
503
504 default:
505 AssertFailed(); /* Not supported by legacy protocol. */
506 rc = VERR_NOT_SUPPORTED;
507 break;
508 }
509
510 *pfRemove = fRemove;
511
512 LogFlowFuncLeaveRC(rc);
513 return rc;
514}
515
516/**
517 * Adds a new message to a client'S message queue.
518 *
519 * @returns IPRT status code.
520 * @param pClient Pointer to the client data structure to add new message to.
521 * @param pMsg Pointer to message to add. The queue then owns the pointer.
522 * @param fAppend Whether to append or prepend the message to the queue.
523 */
524int shclSvcMsgAdd(PSHCLCLIENT pClient, PSHCLCLIENTMSG pMsg, bool fAppend)
525{
526 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
527
528 LogFlowFunc(("uMsg=%RU32 (%s), cParms=%RU32, fAppend=%RTbool\n",
529 pMsg->uMsg, ShClHostMsgToStr(pMsg->uMsg), pMsg->cParms, fAppend));
530
531 if (fAppend)
532 pClient->queueMsg.append(pMsg);
533 else
534 pClient->queueMsg.prepend(pMsg);
535
536 /** @todo Catch / handle OOM? */
537
538 return VINF_SUCCESS;
539}
540
541/**
542 * Initializes a Shared Clipboard client.
543 *
544 * @param pClient Client to initialize.
545 * @param uClientID HGCM client ID to assign client to.
546 */
547int shclSvcClientInit(PSHCLCLIENT pClient, uint32_t uClientID)
548{
549 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
550
551 /* Assign the client ID. */
552 pClient->State.uClientID = uClientID;
553
554 LogFlowFunc(("[Client %RU32]\n", pClient->State.uClientID));
555
556 /* Create the client's own event source. */
557 int rc = ShClEventSourceCreate(&pClient->Events, 0 /* ID, ignored */);
558 if (RT_SUCCESS(rc))
559 {
560 LogFlowFunc(("[Client %RU32] Using event source %RU32\n", uClientID, pClient->Events.uID));
561
562 /* Reset the client state. */
563 shclSvcClientStateReset(&pClient->State);
564
565 /* (Re-)initialize the client state. */
566 rc = shclSvcClientStateInit(&pClient->State, uClientID);
567
568#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
569 if (RT_SUCCESS(rc))
570 rc = ShClTransferCtxInit(&pClient->TransferCtx);
571#endif
572 }
573
574 LogFlowFuncLeaveRC(rc);
575 return rc;
576}
577
578/**
579 * Resets a Shared Clipboard client.
580 *
581 * @param pClient Client to reset.
582 */
583void shclSvcClientReset(PSHCLCLIENT pClient)
584{
585 if (!pClient)
586 return;
587
588 LogFlowFunc(("[Client %RU32]\n", pClient->State.uClientID));
589
590 /* Reset message queue. */
591 shclSvcMsgQueueReset(pClient);
592
593 /* Reset event source. */
594 ShClEventSourceReset(&pClient->Events);
595
596 /* Reset pending state. */
597 RT_ZERO(pClient->Pending);
598
599#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
600 shclSvcClientTransfersReset(pClient);
601#endif
602
603 shclSvcClientStateReset(&pClient->State);
604}
605
606/**
607 * Implements VBOX_SHCL_GUEST_FN_REPORT_FEATURES.
608 *
609 * @returns VBox status code.
610 * @retval VINF_HGCM_ASYNC_EXECUTE on success (we complete the message here).
611 * @retval VERR_ACCESS_DENIED if not master
612 * @retval VERR_INVALID_PARAMETER if bit 63 in the 2nd parameter isn't set.
613 * @retval VERR_WRONG_PARAMETER_COUNT
614 *
615 * @param pClient The client state.
616 * @param hCall The client's call handle.
617 * @param cParms Number of parameters.
618 * @param paParms Array of parameters.
619 */
620int shclSvcClientReportFeatures(PSHCLCLIENT pClient, VBOXHGCMCALLHANDLE hCall,
621 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
622{
623 /*
624 * Validate the request.
625 */
626 ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
627 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
628 uint64_t const fFeatures0 = paParms[0].u.uint64;
629 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
630 uint64_t const fFeatures1 = paParms[1].u.uint64;
631 ASSERT_GUEST_RETURN(fFeatures1 & VBOX_SHCL_GF_1_MUST_BE_ONE, VERR_INVALID_PARAMETER);
632
633 /*
634 * Do the work.
635 */
636 paParms[0].u.uint64 = g_fHostFeatures0;
637 paParms[1].u.uint64 = 0;
638
639 int rc = g_pHelpers->pfnCallComplete(hCall, VINF_SUCCESS);
640 if (RT_SUCCESS(rc))
641 {
642 pClient->State.fGuestFeatures0 = fFeatures0;
643 pClient->State.fGuestFeatures1 = fFeatures1;
644 Log(("[Client %RU32] features: %#RX64 %#RX64\n", pClient->State.uClientID, fFeatures0, fFeatures1));
645
646 /*
647 * Forward the info to Main.
648 */
649 /** @todo Not needed yet. */
650 }
651 else
652 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
653
654 return VINF_HGCM_ASYNC_EXECUTE;
655}
656
657/**
658 * Implements VBOX_SHCL_GUEST_FN_QUERY_FEATURES.
659 *
660 * @returns VBox status code.
661 * @retval VINF_HGCM_ASYNC_EXECUTE on success (we complete the message here).
662 * @retval VERR_WRONG_PARAMETER_COUNT
663 *
664 * @param hCall The client's call handle.
665 * @param cParms Number of parameters.
666 * @param paParms Array of parameters.
667 */
668int shclSvcClientQueryFeatures(VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
669{
670 /*
671 * Validate the request.
672 */
673 ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
674 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
675 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
676 ASSERT_GUEST(paParms[1].u.uint64 & RT_BIT_64(63));
677
678 /*
679 * Do the work.
680 */
681 paParms[0].u.uint64 = g_fHostFeatures0;
682 paParms[1].u.uint64 = 0;
683 int rc = g_pHelpers->pfnCallComplete(hCall, VINF_SUCCESS);
684 if (RT_FAILURE(rc))
685 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
686
687 return VINF_HGCM_ASYNC_EXECUTE;
688}
689
690/**
691 * Implements VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT and VBOX_SHCL_GUEST_FN_MSG_PEEK_NOWAIT.
692 *
693 * @returns VBox status code.
694 * @retval VINF_SUCCESS if a message was pending and is being returned.
695 * @retval VERR_TRY_AGAIN if no message pending and not blocking.
696 * @retval VERR_RESOURCE_BUSY if another read already made a waiting call.
697 * @retval VINF_HGCM_ASYNC_EXECUTE if message wait is pending.
698 *
699 * @param pClient The client state.
700 * @param hCall The client's call handle.
701 * @param cParms Number of parameters.
702 * @param paParms Array of parameters.
703 * @param fWait Set if we should wait for a message, clear if to return
704 * immediately.
705 */
706int shclSvcMsgPeek(PSHCLCLIENT pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[],
707 bool fWait)
708{
709 /*
710 * Validate the request.
711 */
712 ASSERT_GUEST_MSG_RETURN(cParms >= 2, ("cParms=%u!\n", cParms), VERR_WRONG_PARAMETER_COUNT);
713
714 uint64_t idRestoreCheck = 0;
715 uint32_t i = 0;
716 if (paParms[i].type == VBOX_HGCM_SVC_PARM_64BIT)
717 {
718 idRestoreCheck = paParms[0].u.uint64;
719 paParms[0].u.uint64 = 0;
720 i++;
721 }
722 for (; i < cParms; i++)
723 {
724 ASSERT_GUEST_MSG_RETURN(paParms[i].type == VBOX_HGCM_SVC_PARM_32BIT, ("#%u type=%u\n", i, paParms[i].type),
725 VERR_WRONG_PARAMETER_TYPE);
726 paParms[i].u.uint32 = 0;
727 }
728
729 /*
730 * Check restore session ID.
731 */
732 if (idRestoreCheck != 0)
733 {
734 uint64_t idRestore = g_pHelpers->pfnGetVMMDevSessionId(g_pHelpers);
735 if (idRestoreCheck != idRestore)
736 {
737 paParms[0].u.uint64 = idRestore;
738 LogFlowFunc(("[Client %RU32] VBOX_SHCL_GUEST_FN_MSG_PEEK_XXX -> VERR_VM_RESTORED (%#RX64 -> %#RX64)\n",
739 pClient->State.uClientID, idRestoreCheck, idRestore));
740 return VERR_VM_RESTORED;
741 }
742 Assert(!g_pHelpers->pfnIsCallRestored(hCall));
743 }
744
745 /*
746 * Return information about the first message if one is pending in the list.
747 */
748 if (!pClient->queueMsg.isEmpty())
749 {
750 PSHCLCLIENTMSG pFirstMsg = pClient->queueMsg.first();
751 if (pFirstMsg)
752 {
753 shclSvcMsgSetPeekReturn(pFirstMsg, paParms, cParms);
754 LogFlowFunc(("[Client %RU32] VBOX_SHCL_GUEST_FN_MSG_PEEK_XXX -> VINF_SUCCESS (idMsg=%u (%s), cParms=%u)\n",
755 pClient->State.uClientID, pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg),
756 pFirstMsg->cParms));
757 return VINF_SUCCESS;
758 }
759 }
760
761 /*
762 * If we cannot wait, fail the call.
763 */
764 if (!fWait)
765 {
766 LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_NOWAIT -> VERR_TRY_AGAIN\n", pClient->State.uClientID));
767 return VERR_TRY_AGAIN;
768 }
769
770 /*
771 * Wait for the host to queue a message for this client.
772 */
773 ASSERT_GUEST_MSG_RETURN(pClient->Pending.uType == 0, ("Already pending! (idClient=%RU32)\n",
774 pClient->State.uClientID), VERR_RESOURCE_BUSY);
775 pClient->Pending.hHandle = hCall;
776 pClient->Pending.cParms = cParms;
777 pClient->Pending.paParms = paParms;
778 pClient->Pending.uType = VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT;
779 LogFlowFunc(("[Client %RU32] Is now in pending mode...\n", pClient->State.uClientID));
780 return VINF_HGCM_ASYNC_EXECUTE;
781}
782
783/**
784 * Implements VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD.
785 *
786 * @returns VBox status code.
787 * @retval VINF_SUCCESS if a message was pending and is being returned.
788 * @retval VINF_HGCM_ASYNC_EXECUTE if message wait is pending.
789 *
790 * @param pClient The client state.
791 * @param hCall The client's call handle.
792 * @param cParms Number of parameters.
793 * @param paParms Array of parameters.
794 */
795int shclSvcMsgGetOld(PSHCLCLIENT pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
796{
797 int rc;
798
799 if (cParms != VBOX_SHCL_CPARMS_GET_HOST_MSG_OLD)
800 {
801 rc = VERR_INVALID_PARAMETER;
802 }
803 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* msg */
804 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT) /* formats */
805 {
806 rc = VERR_INVALID_PARAMETER;
807 }
808 else
809 {
810 if (!pClient->queueMsg.isEmpty())
811 {
812 PSHCLCLIENTMSG pFirstMsg = pClient->queueMsg.first();
813 AssertPtr(pFirstMsg);
814
815 LogFlowFunc(("[Client %RU32] uMsg=%RU32 (%s), cParms=%RU32\n",
816 pClient->State.uClientID, pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg),
817 pFirstMsg->cParms));
818
819 bool fRemove;
820 rc = shclSvcMsgSetGetHostMsgOldReturn(pFirstMsg, paParms, cParms, &fRemove);
821 if (RT_SUCCESS(rc))
822 {
823 AssertPtr(g_pHelpers);
824 rc = g_pHelpers->pfnCallComplete(hCall, rc);
825 if ( rc != VERR_CANCELLED
826 && fRemove)
827 {
828 pClient->queueMsg.removeFirst();
829 shclSvcMsgFree(pFirstMsg);
830
831 rc = VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
832 }
833 }
834 }
835 else
836 {
837 ASSERT_GUEST_MSG_RETURN(pClient->Pending.uType == 0, ("Already pending! (idClient=%RU32)\n",
838 pClient->State.uClientID), VERR_RESOURCE_BUSY);
839
840 pClient->Pending.hHandle = hCall;
841 pClient->Pending.cParms = cParms;
842 pClient->Pending.paParms = paParms;
843 pClient->Pending.uType = VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD;
844
845 rc = VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
846
847 LogFlowFunc(("[Client %RU32] Is now in pending mode...\n", pClient->State.uClientID));
848 }
849 }
850
851 LogFlowFunc(("[Client %RU32] rc=%Rrc\n", pClient->State.uClientID, rc));
852 return rc;
853}
854
855/**
856 * Implements VBOX_SHCL_GUEST_FN_MSG_GET.
857 *
858 * @returns VBox status code.
859 * @retval VINF_SUCCESS if message retrieved and removed from the pending queue.
860 * @retval VERR_TRY_AGAIN if no message pending.
861 * @retval VERR_BUFFER_OVERFLOW if a parmeter buffer is too small. The buffer
862 * size was updated to reflect the required size, though this isn't yet
863 * forwarded to the guest. (The guest is better of using peek with
864 * parameter count + 2 parameters to get the sizes.)
865 * @retval VERR_MISMATCH if the incoming message ID does not match the pending.
866 * @retval VINF_HGCM_ASYNC_EXECUTE if message was completed already.
867 *
868 * @param pClient The client state.
869 * @param hCall The client's call handle.
870 * @param cParms Number of parameters.
871 * @param paParms Array of parameters.
872 */
873int shclSvcMsgGet(PSHCLCLIENT pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
874{
875 /*
876 * Validate the request.
877 */
878 uint32_t const idMsgExpected = cParms > 0 && paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT ? paParms[0].u.uint32
879 : cParms > 0 && paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT ? paParms[0].u.uint64
880 : UINT32_MAX;
881
882 /*
883 * Return information about the first message if one is pending in the list.
884 */
885 if (!pClient->queueMsg.isEmpty())
886 {
887 PSHCLCLIENTMSG pFirstMsg = pClient->queueMsg.first();
888 if (pFirstMsg)
889 {
890 LogFlowFunc(("First message is: %RU32 (%s), cParms=%RU32\n",
891 pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg), pFirstMsg->cParms));
892
893 ASSERT_GUEST_MSG_RETURN(pFirstMsg->uMsg == idMsgExpected || idMsgExpected == UINT32_MAX,
894 ("idMsg=%u (%s) cParms=%u, caller expected %u (%s) and %u\n",
895 pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg), pFirstMsg->cParms,
896 idMsgExpected, ShClHostMsgToStr(idMsgExpected), cParms),
897 VERR_MISMATCH);
898 ASSERT_GUEST_MSG_RETURN(pFirstMsg->cParms == cParms,
899 ("idMsg=%u (%s) cParms=%u, caller expected %u (%s) and %u\n",
900 pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg), pFirstMsg->cParms,
901 idMsgExpected, ShClHostMsgToStr(idMsgExpected), cParms),
902 VERR_WRONG_PARAMETER_COUNT);
903
904 /* Check the parameter types. */
905 for (uint32_t i = 0; i < cParms; i++)
906 ASSERT_GUEST_MSG_RETURN(pFirstMsg->paParms[i].type == paParms[i].type,
907 ("param #%u: type %u, caller expected %u (idMsg=%u %s)\n", i, pFirstMsg->paParms[i].type,
908 paParms[i].type, pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg)),
909 VERR_WRONG_PARAMETER_TYPE);
910 /*
911 * Copy out the parameters.
912 *
913 * No assertions on buffer overflows, and keep going till the end so we can
914 * communicate all the required buffer sizes.
915 */
916 int rc = VINF_SUCCESS;
917 for (uint32_t i = 0; i < cParms; i++)
918 switch (pFirstMsg->paParms[i].type)
919 {
920 case VBOX_HGCM_SVC_PARM_32BIT:
921 paParms[i].u.uint32 = pFirstMsg->paParms[i].u.uint32;
922 break;
923
924 case VBOX_HGCM_SVC_PARM_64BIT:
925 paParms[i].u.uint64 = pFirstMsg->paParms[i].u.uint64;
926 break;
927
928 case VBOX_HGCM_SVC_PARM_PTR:
929 {
930 uint32_t const cbSrc = pFirstMsg->paParms[i].u.pointer.size;
931 uint32_t const cbDst = paParms[i].u.pointer.size;
932 paParms[i].u.pointer.size = cbSrc; /** @todo Check if this is safe in other layers...
933 * Update: Safe, yes, but VMMDevHGCM doesn't pass it along. */
934 if (cbSrc <= cbDst)
935 memcpy(paParms[i].u.pointer.addr, pFirstMsg->paParms[i].u.pointer.addr, cbSrc);
936 else
937 {
938 AssertMsgFailed(("#%u: cbSrc=%RU32 is bigger than cbDst=%RU32\n", i, cbSrc, cbDst));
939 rc = VERR_BUFFER_OVERFLOW;
940 }
941 break;
942 }
943
944 default:
945 AssertMsgFailed(("#%u: %u\n", i, pFirstMsg->paParms[i].type));
946 rc = VERR_INTERNAL_ERROR;
947 break;
948 }
949 if (RT_SUCCESS(rc))
950 {
951 /*
952 * Complete the message and remove the pending message unless the
953 * guest raced us and cancelled this call in the meantime.
954 */
955 AssertPtr(g_pHelpers);
956 rc = g_pHelpers->pfnCallComplete(hCall, rc);
957
958 LogFlowFunc(("[Client %RU32] pfnCallComplete -> %Rrc\n", pClient->State.uClientID, rc));
959
960 if (rc != VERR_CANCELLED)
961 {
962 pClient->queueMsg.removeFirst();
963 shclSvcMsgFree(pFirstMsg);
964 }
965
966 return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
967 }
968
969 LogFlowFunc(("[Client %RU32] Returning %Rrc\n", pClient->State.uClientID, rc));
970 return rc;
971 }
972 }
973
974 paParms[0].u.uint32 = 0;
975 paParms[1].u.uint32 = 0;
976 LogFlowFunc(("[Client %RU32] -> VERR_TRY_AGAIN\n", pClient->State.uClientID));
977 return VERR_TRY_AGAIN;
978}
979
980/**
981 * Wakes up a pending client (i.e. waiting for new messages).
982 *
983 * @returns VBox status code.
984 * @retval VINF_NO_CHANGE if the client is not in pending mode.
985 *
986 * @param pClient Client to wake up.
987 */
988int shclSvcClientWakeup(PSHCLCLIENT pClient)
989{
990 int rc = VINF_NO_CHANGE;
991
992 if (pClient->Pending.uType)
993 {
994 LogFunc(("[Client %RU32] Waking up ...\n", pClient->State.uClientID));
995
996 rc = VINF_SUCCESS;
997
998 if (!pClient->queueMsg.isEmpty())
999 {
1000 PSHCLCLIENTMSG pFirstMsg = pClient->queueMsg.first();
1001 if (pFirstMsg)
1002 {
1003 LogFunc(("[Client %RU32] Current host message is %RU32 (%s), cParms=%RU32\n",
1004 pClient->State.uClientID, pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg),
1005 pFirstMsg->cParms));
1006
1007 bool fDonePending = false;
1008
1009 if (pClient->Pending.uType == VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT)
1010 {
1011 shclSvcMsgSetPeekReturn(pFirstMsg, pClient->Pending.paParms, pClient->Pending.cParms);
1012 fDonePending = true;
1013 }
1014 else if (pClient->Pending.uType == VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD) /* Legacy, Guest Additions < 6.1. */
1015 {
1016 bool fRemove;
1017 rc = shclSvcMsgSetGetHostMsgOldReturn(pFirstMsg, pClient->Pending.paParms, pClient->Pending.cParms,
1018 &fRemove);
1019 if (RT_SUCCESS(rc))
1020 {
1021 if (fRemove)
1022 {
1023 /* The old (legacy) protocol gets the message right when returning from peeking, so
1024 * remove the actual message from our queue right now. */
1025 pClient->queueMsg.removeFirst();
1026 shclSvcMsgFree(pFirstMsg);
1027 }
1028
1029 fDonePending = true;
1030 }
1031 }
1032
1033 if (fDonePending)
1034 {
1035 rc = g_pHelpers->pfnCallComplete(pClient->Pending.hHandle, VINF_SUCCESS);
1036
1037 pClient->Pending.hHandle = NULL;
1038 pClient->Pending.paParms = NULL;
1039 pClient->Pending.cParms = 0;
1040 pClient->Pending.uType = 0;
1041 }
1042 }
1043 else
1044 AssertFailed();
1045 }
1046 else
1047 AssertMsgFailed(("Waking up client ID=%RU32 with no host message in queue is a bad idea\n", pClient->State.uClientID));
1048
1049 return rc;
1050 }
1051 else
1052 LogFunc(("[Client %RU32] Not in pending state, skipping wakeup\n", pClient->State.uClientID));
1053
1054 return VINF_NO_CHANGE;
1055}
1056
1057/**
1058 * Requests to read clipboard data from the guest.
1059 *
1060 * @returns VBox status code.
1061 * @param pClient Client to request to read data form.
1062 * @param pDataReq Data request to send to the guest.
1063 * @param puEvent Event ID for waiting for new data. Optional.
1064 */
1065int ShClSvcDataReadRequest(PSHCLCLIENT pClient, PSHCLDATAREQ pDataReq,
1066 PSHCLEVENTID puEvent)
1067{
1068 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
1069 AssertPtrReturn(pDataReq, VERR_INVALID_POINTER);
1070 /* puEvent is optional. */
1071
1072 LogFlowFuncEnter();
1073
1074 int rc;
1075
1076 PSHCLCLIENTMSG pMsgReadData = shclSvcMsgAlloc(VBOX_SHCL_HOST_MSG_READ_DATA,
1077 VBOX_SHCL_CPARMS_READ_DATA_REQ);
1078 if (pMsgReadData)
1079 {
1080 const SHCLEVENTID uEvent = ShClEventIDGenerate(&pClient->Events);
1081
1082 LogFlowFunc(("uFmt=0x%x\n", pDataReq->uFmt));
1083
1084 HGCMSvcSetU64(&pMsgReadData->paParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
1085 pClient->Events.uID, uEvent));
1086 HGCMSvcSetU32(&pMsgReadData->paParms[1], 0 /* fFlags */);
1087 HGCMSvcSetU32(&pMsgReadData->paParms[2], pDataReq->uFmt);
1088 HGCMSvcSetU32(&pMsgReadData->paParms[3], pClient->State.cbChunkSize);
1089
1090 rc = shclSvcMsgAdd(pClient, pMsgReadData, true /* fAppend */);
1091 if (RT_SUCCESS(rc))
1092 {
1093 rc = ShClEventRegister(&pClient->Events, uEvent);
1094 if (RT_SUCCESS(rc))
1095 {
1096 rc = shclSvcClientWakeup(pClient);
1097 if (RT_SUCCESS(rc))
1098 {
1099 if (puEvent)
1100 *puEvent = uEvent;
1101 }
1102 else
1103 ShClEventUnregister(&pClient->Events, uEvent);
1104 }
1105 }
1106 }
1107 else
1108 rc = VERR_NO_MEMORY;
1109
1110 LogFlowFuncLeaveRC(rc);
1111 return rc;
1112}
1113
1114int ShClSvcDataReadSignal(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
1115 PSHCLDATABLOCK pData)
1116{
1117 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
1118 AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
1119 AssertPtrReturn(pData, VERR_INVALID_POINTER);
1120
1121 LogFlowFuncEnter();
1122
1123 SHCLEVENTID uEvent;
1124 if (!(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID)) /* Legacy, Guest Additions < 6.1. */
1125 {
1126 /* Older Guest Additions (<= VBox 6.0) did not have any context ID handling, so we ASSUME that the last event registered
1127 * is the one we want to handle (as this all was a synchronous protocol anyway). */
1128 uEvent = ShClEventGetLast(&pClient->Events);
1129 }
1130 else
1131 uEvent = VBOX_SHCL_CONTEXTID_GET_EVENT(pCmdCtx->uContextID);
1132
1133 int rc = VINF_SUCCESS;
1134
1135 PSHCLEVENTPAYLOAD pPayload = NULL;
1136 if (pData->cbData)
1137 rc = ShClPayloadAlloc(uEvent, pData->pvData, pData->cbData, &pPayload);
1138
1139 if (RT_SUCCESS(rc))
1140 {
1141 rc = ShClEventSignal(&pClient->Events, uEvent, pPayload);
1142 if (RT_FAILURE(rc))
1143 ShClPayloadFree(pPayload);
1144 }
1145
1146 LogFlowFuncLeaveRC(rc);
1147 return rc;
1148}
1149
1150int ShClSvcFormatsReport(PSHCLCLIENT pClient, PSHCLFORMATDATA pFormats)
1151{
1152 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
1153 AssertPtrReturn(pFormats, VERR_INVALID_POINTER);
1154
1155 LogFlowFuncEnter();
1156
1157 int rc;
1158
1159 PSHCLCLIENTMSG pMsg = shclSvcMsgAlloc(VBOX_SHCL_HOST_MSG_FORMATS_REPORT, 3);
1160 if (pMsg)
1161 {
1162 const SHCLEVENTID uEvent = ShClEventIDGenerate(&pClient->Events);
1163
1164 HGCMSvcSetU64(&pMsg->paParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
1165 pClient->Events.uID, uEvent));
1166 HGCMSvcSetU32(&pMsg->paParms[1], pFormats->uFormats);
1167 HGCMSvcSetU32(&pMsg->paParms[2], 0 /* fFlags */);
1168
1169 rc = shclSvcMsgAdd(pClient, pMsg, true /* fAppend */);
1170 if (RT_SUCCESS(rc))
1171 {
1172#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1173 /* If this is an URI list, create a transfer locally and also tell the guest to create
1174 * a transfer on the guest side. */
1175 if (pFormats->uFormats & VBOX_SHCL_FMT_URI_LIST)
1176 {
1177 rc = shClSvcTransferStart(pClient, SHCLTRANSFERDIR_WRITE, SHCLSOURCE_LOCAL,
1178 NULL /* pTransfer */);
1179 if (RT_FAILURE(rc))
1180 LogRel(("Shared Clipboard: Initializing host write transfer failed with %Rrc\n", rc));
1181 }
1182 else
1183 {
1184#endif
1185 pClient->State.fFlags |= SHCLCLIENTSTATE_FLAGS_READ_ACTIVE;
1186
1187 rc = shclSvcClientWakeup(pClient);
1188#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1189 }
1190#endif
1191 }
1192 }
1193 else
1194 rc = VERR_NO_MEMORY;
1195
1196 LogFlowFuncLeaveRC(rc);
1197 return rc;
1198}
1199
1200int shClSvcGetDataRead(PSHCLCLIENT pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1201{
1202 LogFlowFuncEnter();
1203
1204 if ( ShClSvcGetMode() != VBOX_SHCL_MODE_HOST_TO_GUEST
1205 && ShClSvcGetMode() != VBOX_SHCL_MODE_BIDIRECTIONAL)
1206 {
1207 return VERR_ACCESS_DENIED;
1208 }
1209
1210 /* Is the guest supposed to read any clipboard data from the host? */
1211 if (!(pClient->State.fFlags & SHCLCLIENTSTATE_FLAGS_READ_ACTIVE))
1212 return VERR_WRONG_ORDER;
1213
1214 int rc;
1215
1216 SHCLCLIENTCMDCTX cmdCtx;
1217 RT_ZERO(cmdCtx);
1218
1219 SHCLDATABLOCK dataBlock;
1220 RT_ZERO(dataBlock);
1221
1222 if (!(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID)) /* Legacy, Guest Additions < 6.1. */
1223 {
1224 if (cParms != 3)
1225 {
1226 rc = VERR_INVALID_PARAMETER;
1227 }
1228 else
1229 {
1230 rc = HGCMSvcGetU32(&paParms[0], &dataBlock.uFormat);
1231 if (RT_SUCCESS(rc))
1232 {
1233 if (pClient->State.POD.uFormat == VBOX_SHCL_FMT_NONE)
1234 pClient->State.POD.uFormat = dataBlock.uFormat;
1235
1236 if (dataBlock.uFormat != pClient->State.POD.uFormat)
1237 {
1238 LogFlowFunc(("Invalid format (pClient->State.POD.uFormat=%RU32 vs dataBlock.uFormat=%RU32)\n",
1239 pClient->State.POD.uFormat, dataBlock.uFormat));
1240
1241 rc = VERR_INVALID_PARAMETER;
1242 }
1243 else
1244 {
1245 rc = HGCMSvcGetBuf(&paParms[1], &dataBlock.pvData, &dataBlock.cbData);
1246 }
1247 }
1248 }
1249 }
1250 else
1251 {
1252 if (cParms < VBOX_SHCL_CPARMS_READ_DATA)
1253 {
1254 rc = VERR_INVALID_PARAMETER;
1255 }
1256 else
1257 {
1258 /** @todo Handle paParms[1] flags. */
1259
1260 rc = HGCMSvcGetU32(&paParms[2], &dataBlock.uFormat);
1261 if (RT_SUCCESS(rc))
1262 {
1263 uint32_t cbData;
1264 rc = HGCMSvcGetU32(&paParms[3], &cbData);
1265 if (RT_SUCCESS(rc))
1266 {
1267 rc = HGCMSvcGetBuf(&paParms[4], &dataBlock.pvData, &dataBlock.cbData);
1268 if (RT_SUCCESS(rc))
1269 {
1270 if (cbData != dataBlock.cbData)
1271 {
1272 LogFlowFunc(("Invalid data (cbData=%RU32 vs dataBlock.cbData=%RU32)\n", cbData, dataBlock.cbData));
1273 rc = VERR_INVALID_PARAMETER;
1274 }
1275 }
1276 }
1277 }
1278 }
1279 }
1280
1281 if (RT_SUCCESS(rc))
1282 {
1283 uint32_t cbActual = 0;
1284
1285 /* If there is a service extension active, try reading data from it first. */
1286 if (g_ExtState.pfnExtension)
1287 {
1288 SHCLEXTPARMS parms;
1289 RT_ZERO(parms);
1290
1291 parms.uFormat = pClient->State.POD.uFormat;
1292 parms.u.pvData = dataBlock.pvData;
1293 parms.cbData = dataBlock.cbData;
1294
1295 g_ExtState.fReadingData = true;
1296
1297 /* Read clipboard data from the extension. */
1298 rc = g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_READ,
1299 &parms, sizeof(parms));
1300
1301 LogFlowFunc(("g_ExtState.fDelayedAnnouncement=%RTbool, g_ExtState.uDelayedFormats=0x%x\n",
1302 g_ExtState.fDelayedAnnouncement, g_ExtState.uDelayedFormats));
1303
1304 /* Did the extension send the clipboard formats yet?
1305 * Otherwise, do this now. */
1306 if (g_ExtState.fDelayedAnnouncement)
1307 {
1308 SHCLFORMATDATA formatData;
1309 RT_ZERO(formatData);
1310
1311 formatData.uFormats = g_ExtState.uDelayedFormats;
1312 Assert(formatData.uFormats != VBOX_SHCL_FMT_NONE); /* There better is *any* format here now. */
1313
1314 int rc2 = ShClSvcFormatsReport(pClient, &formatData);
1315 AssertRC(rc2);
1316
1317 g_ExtState.fDelayedAnnouncement = false;
1318 g_ExtState.uDelayedFormats = 0;
1319 }
1320
1321 g_ExtState.fReadingData = false;
1322
1323 if (RT_SUCCESS(rc))
1324 {
1325 cbActual = parms.cbData;
1326 }
1327 }
1328
1329 /* Note: The host clipboard *always* has precedence over the service extension above,
1330 * so data which has been read above might get overridden by the host clipboard eventually. */
1331
1332 if (RT_SUCCESS(rc))
1333 {
1334 rc = ShClSvcImplReadData(pClient, &cmdCtx, &dataBlock, &cbActual);
1335 if (RT_SUCCESS(rc))
1336 {
1337 LogFlowFunc(("cbData=%RU32, cbActual=%RU32\n", dataBlock.cbData, cbActual));
1338
1339 /* Return the actual size required to fullfil the request. */
1340 if (!(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID)) /* Legacy, Guest Additions < 6.1. */
1341 {
1342 HGCMSvcSetU32(&paParms[2], cbActual);
1343 }
1344 else
1345 {
1346 HGCMSvcSetU32(&paParms[3], cbActual);
1347 }
1348
1349 /* If the data to return exceeds the buffer the guest supplies, tell it (and let it try again). */
1350 if (cbActual >= dataBlock.cbData)
1351 rc = VINF_BUFFER_OVERFLOW;
1352
1353 if (rc == VINF_SUCCESS)
1354 {
1355 /* Only remove "read active" flag after successful read again. */
1356 pClient->State.fFlags &= ~SHCLCLIENTSTATE_FLAGS_READ_ACTIVE;
1357 }
1358 }
1359 }
1360 }
1361
1362 LogFlowFuncLeaveRC(rc);
1363 return rc;
1364}
1365
1366int shClSvcGetDataWrite(PSHCLCLIENT pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1367{
1368 LogFlowFuncEnter();
1369
1370 if ( ShClSvcGetMode() != VBOX_SHCL_MODE_GUEST_TO_HOST
1371 && ShClSvcGetMode() != VBOX_SHCL_MODE_BIDIRECTIONAL)
1372 {
1373 return VERR_ACCESS_DENIED;
1374 }
1375
1376 /* Is the guest supposed to write any clipboard data from the host? */
1377 if (!(pClient->State.fFlags & SHCLCLIENTSTATE_FLAGS_WRITE_ACTIVE))
1378 return VERR_WRONG_ORDER;
1379
1380 int rc;
1381
1382 SHCLDATABLOCK dataBlock;
1383 RT_ZERO(dataBlock);
1384
1385 SHCLCLIENTCMDCTX cmdCtx;
1386 RT_ZERO(cmdCtx);
1387
1388 if (!(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID)) /* Legacy, Guest Additions < 6.1. */
1389 {
1390 if (cParms != 2)
1391 {
1392 rc = VERR_INVALID_PARAMETER;
1393 }
1394 else
1395 {
1396 rc = HGCMSvcGetU32(&paParms[0], &dataBlock.uFormat);
1397 if (RT_SUCCESS(rc))
1398 {
1399 if (pClient->State.POD.uFormat == VBOX_SHCL_FMT_NONE)
1400 pClient->State.POD.uFormat = dataBlock.uFormat;
1401
1402 if ( dataBlock.uFormat == VBOX_SHCL_FMT_NONE
1403 || dataBlock.uFormat != pClient->State.POD.uFormat)
1404 {
1405 LogFunc(("Invalid format (client=%RU32 vs host=%RU32)\n", dataBlock.uFormat, pClient->State.POD.uFormat));
1406 rc = VERR_INVALID_PARAMETER;
1407 }
1408 else
1409 rc = HGCMSvcGetBuf(&paParms[1], &dataBlock.pvData, &dataBlock.cbData);
1410 }
1411 }
1412 }
1413 else
1414 {
1415 if (cParms != VBOX_SHCL_CPARMS_WRITE_DATA)
1416 {
1417 rc = VERR_INVALID_PARAMETER;
1418 }
1419 else
1420 {
1421 rc = HGCMSvcGetU64(&paParms[0], &cmdCtx.uContextID);
1422
1423 /** @todo Handle paParms[1] flags. */
1424
1425 if (RT_SUCCESS(rc))
1426 rc = HGCMSvcGetU32(&paParms[2], &dataBlock.uFormat);
1427 if (RT_SUCCESS(rc))
1428 rc = HGCMSvcGetBuf(&paParms[4], &dataBlock.pvData, &dataBlock.cbData);
1429 }
1430 }
1431
1432 if (RT_SUCCESS(rc))
1433 {
1434 if (g_ExtState.pfnExtension)
1435 {
1436 SHCLEXTPARMS parms;
1437 RT_ZERO(parms);
1438
1439 parms.uFormat = dataBlock.uFormat;
1440 parms.u.pvData = dataBlock.pvData;
1441 parms.cbData = dataBlock.cbData;
1442
1443 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_WRITE, &parms, sizeof(parms));
1444 }
1445
1446 rc = ShClSvcImplWriteData(pClient, &cmdCtx, &dataBlock);
1447 if (RT_SUCCESS(rc))
1448 {
1449 /* Remove "write active" flag after successful read again. */
1450 pClient->State.fFlags &= ~SHCLCLIENTSTATE_FLAGS_WRITE_ACTIVE;
1451 }
1452 }
1453
1454 LogFlowFuncLeaveRC(rc);
1455 return rc;
1456}
1457
1458/**
1459 * Gets an error from HGCM service parameters.
1460 *
1461 * @returns VBox status code.
1462 * @param cParms Number of HGCM parameters supplied in \a paParms.
1463 * @param paParms Array of HGCM parameters.
1464 * @param pRc Where to store the received error code.
1465 */
1466static int shclSvcGetError(uint32_t cParms, VBOXHGCMSVCPARM paParms[], int *pRc)
1467{
1468 AssertPtrReturn(paParms, VERR_INVALID_PARAMETER);
1469 AssertPtrReturn(pRc, VERR_INVALID_PARAMETER);
1470
1471 int rc;
1472
1473 if (cParms == VBOX_SHCL_CPARMS_ERROR)
1474 {
1475 rc = HGCMSvcGetU32(&paParms[1], (uint32_t *)pRc); /** @todo int vs. uint32_t !!! */
1476 }
1477 else
1478 rc = VERR_INVALID_PARAMETER;
1479
1480 LogFlowFuncLeaveRC(rc);
1481 return rc;
1482}
1483
1484int shClSvcSetSource(PSHCLCLIENT pClient, SHCLSOURCE enmSource)
1485{
1486 if (!pClient) /* If no client connected (anymore), bail out. */
1487 return VINF_SUCCESS;
1488
1489 int rc = VINF_SUCCESS;
1490
1491 if (ShClSvcLock())
1492 {
1493 pClient->State.enmSource = enmSource;
1494
1495 LogFlowFunc(("Source of client %RU32 is now %RU32\n", pClient->State.uClientID, pClient->State.enmSource));
1496
1497 ShClSvcUnlock();
1498 }
1499
1500 LogFlowFuncLeaveRC(rc);
1501 return rc;
1502}
1503
1504static int svcInit(void)
1505{
1506 int rc = RTCritSectInit(&g_CritSect);
1507
1508 if (RT_SUCCESS(rc))
1509 {
1510 shClSvcModeSet(VBOX_SHCL_MODE_OFF);
1511
1512 rc = ShClSvcImplInit();
1513
1514 /* Clean up on failure, because 'svnUnload' will not be called
1515 * if the 'svcInit' returns an error.
1516 */
1517 if (RT_FAILURE(rc))
1518 {
1519 RTCritSectDelete(&g_CritSect);
1520 }
1521 }
1522
1523 return rc;
1524}
1525
1526static DECLCALLBACK(int) svcUnload(void *)
1527{
1528 LogFlowFuncEnter();
1529
1530 ShClSvcImplDestroy();
1531
1532 RTCritSectDelete(&g_CritSect);
1533
1534 return VINF_SUCCESS;
1535}
1536
1537static DECLCALLBACK(int) svcDisconnect(void *, uint32_t u32ClientID, void *pvClient)
1538{
1539 LogFunc(("u32ClientID=%RU32\n", u32ClientID));
1540
1541 PSHCLCLIENT pClient = (PSHCLCLIENT)pvClient;
1542 AssertPtr(pClient);
1543
1544#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1545 shclSvcClientTransfersReset(pClient);
1546#endif
1547
1548 ShClSvcImplDisconnect(pClient);
1549
1550 /* Make sure to send a quit message to the guest so that it can terminate gracefully. */
1551 if (pClient->Pending.uType)
1552 {
1553 if (pClient->Pending.cParms >= 2)
1554 {
1555 HGCMSvcSetU32(&pClient->Pending.paParms[0], VBOX_SHCL_HOST_MSG_QUIT);
1556 HGCMSvcSetU32(&pClient->Pending.paParms[1], 0);
1557 }
1558 g_pHelpers->pfnCallComplete(pClient->Pending.hHandle, VINF_SUCCESS);
1559 pClient->Pending.uType = 0;
1560 }
1561
1562 shclSvcClientStateDestroy(&pClient->State);
1563 ShClEventSourceDestroy(&pClient->Events);
1564
1565 ClipboardClientMap::iterator itClient = g_mapClients.find(u32ClientID);
1566 if (itClient != g_mapClients.end())
1567 {
1568 g_mapClients.erase(itClient);
1569 }
1570 else
1571 AssertFailed();
1572
1573 return VINF_SUCCESS;
1574}
1575
1576static DECLCALLBACK(int) svcConnect(void *, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring)
1577{
1578 RT_NOREF(fRequestor, fRestoring);
1579
1580 PSHCLCLIENT pClient = (PSHCLCLIENT)pvClient;
1581 AssertPtr(pvClient);
1582
1583 int rc = shclSvcClientInit(pClient, u32ClientID);
1584 if (RT_SUCCESS(rc))
1585 {
1586 rc = ShClSvcImplConnect(pClient, ShClSvcGetHeadless());
1587 if (RT_SUCCESS(rc))
1588 {
1589 /* Assign weak pointer to client map .*/
1590 g_mapClients[u32ClientID] = pClient; /** @todo Handle OOM / collisions? */
1591
1592 /* For now we ASSUME that the first client ever connected is in charge for
1593 * communicating withe the service extension.
1594 *
1595 ** @todo This needs to be fixed ASAP w/o breaking older guest / host combos. */
1596 if (g_ExtState.uClientID == 0)
1597 g_ExtState.uClientID = u32ClientID;
1598 }
1599 }
1600
1601 LogFlowFuncLeaveRC(rc);
1602 return rc;
1603}
1604
1605static DECLCALLBACK(void) svcCall(void *,
1606 VBOXHGCMCALLHANDLE callHandle,
1607 uint32_t u32ClientID,
1608 void *pvClient,
1609 uint32_t u32Function,
1610 uint32_t cParms,
1611 VBOXHGCMSVCPARM paParms[],
1612 uint64_t tsArrival)
1613{
1614 RT_NOREF(u32ClientID, pvClient, tsArrival);
1615
1616 int rc = VINF_SUCCESS;
1617
1618 PSHCLCLIENT pClient = (PSHCLCLIENT)pvClient;
1619 AssertPtr(pClient);
1620
1621 LogFunc(("u32ClientID=%RU32, fn=%RU32 (%s), cParms=%RU32, paParms=%p\n",
1622 u32ClientID, u32Function, ShClGuestMsgToStr(u32Function), cParms, paParms));
1623
1624#ifdef DEBUG
1625 uint32_t i;
1626
1627 for (i = 0; i < cParms; i++)
1628 {
1629 /** @todo parameters other than 32 bit */
1630 LogFunc((" paParms[%d]: type %RU32 - value %RU32\n", i, paParms[i].type, paParms[i].u.uint32));
1631 }
1632#endif
1633
1634 LogFunc(("Client state: fFlags=0x%x, fGuestFeatures0=0x%x, fGuestFeatures1=0x%x\n",
1635 pClient->State.fFlags, pClient->State.fGuestFeatures0, pClient->State.fGuestFeatures1));
1636
1637 switch (u32Function)
1638 {
1639 case VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD:
1640 {
1641 rc = shclSvcMsgGetOld(pClient, callHandle, cParms, paParms);
1642 break;
1643 }
1644
1645 case VBOX_SHCL_GUEST_FN_CONNECT:
1646 {
1647 if (cParms != VBOX_SHCL_CPARMS_CONNECT)
1648 {
1649 rc = VERR_INVALID_PARAMETER;
1650 }
1651 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* cbChunkSize */
1652 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* enmCompression */
1653 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT) /* enmChecksumType */
1654 {
1655 rc = VERR_INVALID_PARAMETER;
1656 }
1657 else if (ShClSvcGetMode() == VBOX_SHCL_MODE_OFF)
1658 {
1659 rc = VERR_ACCESS_DENIED;
1660 }
1661 else
1662 {
1663 /* Report back supported chunk size to the guest. */
1664 HGCMSvcSetU32(&paParms[0], _64K); /* Chunk size */ /** @todo Make chunk size dynamic. */
1665
1666 rc = VINF_SUCCESS;
1667 }
1668
1669 break;
1670 }
1671
1672 case VBOX_SHCL_GUEST_FN_REPORT_FEATURES:
1673 {
1674 rc = shclSvcClientReportFeatures(pClient, callHandle, cParms, paParms);
1675 break;
1676 }
1677
1678 case VBOX_SHCL_GUEST_FN_QUERY_FEATURES:
1679 {
1680 rc = shclSvcClientQueryFeatures(callHandle, cParms, paParms);
1681 break;
1682 }
1683
1684 case VBOX_SHCL_GUEST_FN_MSG_PEEK_NOWAIT:
1685 {
1686 rc = shclSvcMsgPeek(pClient, callHandle, cParms, paParms, false /*fWait*/);
1687 break;
1688 }
1689
1690 case VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT:
1691 {
1692 rc = shclSvcMsgPeek(pClient, callHandle, cParms, paParms, true /*fWait*/);
1693 break;
1694 }
1695
1696 case VBOX_SHCL_GUEST_FN_MSG_GET:
1697 {
1698 rc = shclSvcMsgGet(pClient, callHandle, cParms, paParms);
1699 break;
1700 }
1701
1702 case VBOX_SHCL_GUEST_FN_FORMATS_REPORT:
1703 {
1704 uint32_t uFormats = 0;
1705
1706 if (!(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID)) /* Legacy, Guest Additions < 6.1. */
1707 {
1708 if (cParms != 1)
1709 {
1710 rc = VERR_INVALID_PARAMETER;
1711 }
1712 else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT) /* uFormats */
1713 {
1714 rc = VERR_INVALID_PARAMETER;
1715 }
1716 else
1717 {
1718 rc = HGCMSvcGetU32(&paParms[0], &uFormats);
1719 }
1720 }
1721 else
1722 {
1723 if (cParms != 3)
1724 {
1725 rc = VERR_INVALID_PARAMETER;
1726 }
1727 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_64BIT /* uContextID */
1728 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* uFormats */
1729 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT) /* fFlags */
1730 {
1731 rc = VERR_INVALID_PARAMETER;
1732 }
1733 else
1734 {
1735 rc = HGCMSvcGetU32(&paParms[1], &uFormats);
1736
1737 /** @todo Handle rest. */
1738 }
1739 }
1740
1741 if (RT_SUCCESS(rc))
1742 {
1743 if ( ShClSvcGetMode() != VBOX_SHCL_MODE_GUEST_TO_HOST
1744 && ShClSvcGetMode() != VBOX_SHCL_MODE_BIDIRECTIONAL)
1745 {
1746 rc = VERR_ACCESS_DENIED;
1747 }
1748 else if (uFormats != VBOX_SHCL_FMT_NONE) /* Only announce formats if we actually *have* formats to announce! */
1749 {
1750 rc = shClSvcSetSource(pClient, SHCLSOURCE_REMOTE);
1751 if (RT_SUCCESS(rc))
1752 {
1753 if (g_ExtState.pfnExtension)
1754 {
1755 SHCLEXTPARMS parms;
1756 RT_ZERO(parms);
1757
1758 parms.uFormat = uFormats;
1759
1760 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE,
1761 &parms, sizeof(parms));
1762 }
1763
1764 SHCLCLIENTCMDCTX cmdCtx;
1765 RT_ZERO(cmdCtx);
1766
1767 SHCLFORMATDATA formatData;
1768 RT_ZERO(formatData);
1769
1770 formatData.uFormats = uFormats;
1771 Assert(formatData.uFormats != VBOX_SHCL_FMT_NONE); /* Sanity. */
1772
1773 rc = ShClSvcImplFormatAnnounce(pClient, &cmdCtx, &formatData);
1774 if (RT_SUCCESS(rc))
1775 {
1776 pClient->State.fFlags |= SHCLCLIENTSTATE_FLAGS_WRITE_ACTIVE;
1777 }
1778 }
1779 }
1780 }
1781
1782 break;
1783 }
1784
1785 case VBOX_SHCL_GUEST_FN_DATA_READ:
1786 {
1787 rc = shClSvcGetDataRead(pClient, cParms, paParms);
1788 break;
1789 }
1790
1791 case VBOX_SHCL_GUEST_FN_DATA_WRITE:
1792 {
1793 rc = shClSvcGetDataWrite(pClient, cParms, paParms);
1794 break;
1795 }
1796
1797 case VBOX_SHCL_GUEST_FN_CANCEL:
1798 {
1799 LogRel2(("Shared Clipboard: Operation canceled by guest side\n"));
1800
1801 /* Reset client state and start over. */
1802 shclSvcClientStateReset(&pClient->State);
1803#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1804 shclSvcClientTransfersReset(pClient);
1805#endif
1806 /** @todo Do we need to do anything else here? */
1807 break;
1808 }
1809
1810 case VBOX_SHCL_GUEST_FN_ERROR:
1811 {
1812 int rcGuest;
1813 rc = shclSvcGetError(cParms,paParms, &rcGuest);
1814 if (RT_SUCCESS(rc))
1815 {
1816 LogRel(("Shared Clipboard: Error from guest side: %Rrc\n", rcGuest));
1817
1818 /* Reset client state and start over. */
1819 shclSvcClientStateReset(&pClient->State);
1820#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1821 shclSvcClientTransfersReset(pClient);
1822#endif
1823 }
1824 break;
1825 }
1826
1827 default:
1828 {
1829#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1830 if (g_fTransferMode & VBOX_SHCL_TRANSFER_MODE_ENABLED)
1831 {
1832 rc = shClSvcTransferHandler(pClient, callHandle, u32Function, cParms, paParms, tsArrival);
1833 }
1834 else
1835 {
1836 LogRel2(("Shared Clipboard: File transfers are disabled for this VM\n"));
1837 rc = VERR_ACCESS_DENIED;
1838 }
1839#else
1840 rc = VERR_NOT_SUPPORTED;
1841#endif
1842 break;
1843 }
1844 }
1845
1846 LogFlowFunc(("[Client %RU32] rc=%Rrc\n", pClient->State.uClientID, rc));
1847
1848 if (rc != VINF_HGCM_ASYNC_EXECUTE)
1849 g_pHelpers->pfnCallComplete(callHandle, rc);
1850}
1851
1852/**
1853 * Initializes a Shared Clipboard service's client state.
1854 *
1855 * @returns VBox status code.
1856 * @param pClientState Client state to initialize.
1857 * @param uClientID Client ID (HGCM) to use for this client state.
1858 */
1859int shclSvcClientStateInit(PSHCLCLIENTSTATE pClientState, uint32_t uClientID)
1860{
1861 LogFlowFuncEnter();
1862
1863 shclSvcClientStateReset(pClientState);
1864
1865 /* Register the client. */
1866 pClientState->uClientID = uClientID;
1867
1868 return VINF_SUCCESS;
1869}
1870
1871/**
1872 * Destroys a Shared Clipboard service's client state.
1873 *
1874 * @returns VBox status code.
1875 * @param pClientState Client state to destroy.
1876 */
1877int shclSvcClientStateDestroy(PSHCLCLIENTSTATE pClientState)
1878{
1879 RT_NOREF(pClientState);
1880
1881 LogFlowFuncEnter();
1882
1883 return VINF_SUCCESS;
1884}
1885
1886/**
1887 * Resets a Shared Clipboard service's client state.
1888 *
1889 * @param pClientState Client state to reset.
1890 */
1891void shclSvcClientStateReset(PSHCLCLIENTSTATE pClientState)
1892{
1893 LogFlowFuncEnter();
1894
1895 pClientState->fGuestFeatures0 = VBOX_SHCL_GF_NONE;
1896 pClientState->fGuestFeatures1 = VBOX_SHCL_GF_NONE;
1897
1898 pClientState->cbChunkSize = _64K; /** Make this configurable. */
1899 pClientState->enmSource = SHCLSOURCE_INVALID;
1900 pClientState->fFlags = SHCLCLIENTSTATE_FLAGS_NONE;
1901
1902 pClientState->POD.enmDir = SHCLTRANSFERDIR_UNKNOWN;
1903 pClientState->POD.uFormat = VBOX_SHCL_FMT_NONE;
1904 pClientState->POD.cbToReadWriteTotal = 0;
1905 pClientState->POD.cbReadWritten = 0;
1906
1907#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1908 pClientState->Transfers.enmTransferDir = SHCLTRANSFERDIR_UNKNOWN;
1909#endif
1910
1911
1912}
1913
1914/*
1915 * We differentiate between a function handler for the guest and one for the host.
1916 */
1917static DECLCALLBACK(int) svcHostCall(void *,
1918 uint32_t u32Function,
1919 uint32_t cParms,
1920 VBOXHGCMSVCPARM paParms[])
1921{
1922 int rc = VINF_SUCCESS;
1923
1924 LogFlowFunc(("u32Function=%RU32 (%s), cParms=%RU32, paParms=%p\n",
1925 u32Function, ShClHostFunctionToStr(u32Function), cParms, paParms));
1926
1927 switch (u32Function)
1928 {
1929 case VBOX_SHCL_HOST_FN_SET_MODE:
1930 {
1931 if (cParms != 1)
1932 {
1933 rc = VERR_INVALID_PARAMETER;
1934 }
1935 else
1936 {
1937 uint32_t u32Mode = VBOX_SHCL_MODE_OFF;
1938
1939 rc = HGCMSvcGetU32(&paParms[0], &u32Mode);
1940 if (RT_SUCCESS(rc))
1941 rc = shClSvcModeSet(u32Mode);
1942 }
1943
1944 break;
1945 }
1946
1947#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1948 case VBOX_SHCL_HOST_FN_SET_TRANSFER_MODE:
1949 {
1950 if (cParms != 1)
1951 {
1952 rc = VERR_INVALID_PARAMETER;
1953 }
1954 else
1955 {
1956 uint32_t fTransferMode;
1957 rc = HGCMSvcGetU32(&paParms[0], &fTransferMode);
1958 if (RT_SUCCESS(rc))
1959 rc = shClSvcTransferModeSet(fTransferMode);
1960 }
1961 break;
1962 }
1963#endif
1964 case VBOX_SHCL_HOST_FN_SET_HEADLESS:
1965 {
1966 if (cParms != 1)
1967 {
1968 rc = VERR_INVALID_PARAMETER;
1969 }
1970 else
1971 {
1972 uint32_t uHeadless;
1973 rc = HGCMSvcGetU32(&paParms[0], &uHeadless);
1974 if (RT_SUCCESS(rc))
1975 {
1976 g_fHeadless = RT_BOOL(uHeadless);
1977 LogRel(("Shared Clipboard: Service running in %s mode\n", g_fHeadless ? "headless" : "normal"));
1978 }
1979 }
1980 break;
1981 }
1982
1983 default:
1984 {
1985#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1986 rc = shClSvcTransferHostHandler(u32Function, cParms, paParms);
1987#else
1988 rc = VERR_NOT_IMPLEMENTED;
1989#endif
1990 break;
1991 }
1992 }
1993
1994 LogFlowFuncLeaveRC(rc);
1995 return rc;
1996}
1997
1998#ifndef UNIT_TEST
1999/**
2000 * SSM descriptor table for the SHCLCLIENTSTATE structure.
2001 */
2002static SSMFIELD const s_aShClSSMClientState[] =
2003{
2004 /** Note: Saving the session ID not necessary, as they're not persistent across state save/restore. */
2005 SSMFIELD_ENTRY(SHCLCLIENTSTATE, fGuestFeatures0),
2006 SSMFIELD_ENTRY(SHCLCLIENTSTATE, fGuestFeatures1),
2007 SSMFIELD_ENTRY(SHCLCLIENTSTATE, cbChunkSize),
2008 SSMFIELD_ENTRY(SHCLCLIENTSTATE, enmSource),
2009 SSMFIELD_ENTRY(SHCLCLIENTSTATE, fFlags),
2010 SSMFIELD_ENTRY_TERM()
2011};
2012
2013/**
2014 * SSM descriptor table for the SHCLCLIENTPODSTATE structure.
2015 */
2016static SSMFIELD const s_aShClSSMClientPODState[] =
2017{
2018 SSMFIELD_ENTRY(SHCLCLIENTPODSTATE, enmDir),
2019 SSMFIELD_ENTRY(SHCLCLIENTPODSTATE, uFormat),
2020 SSMFIELD_ENTRY(SHCLCLIENTPODSTATE, cbToReadWriteTotal),
2021 SSMFIELD_ENTRY(SHCLCLIENTPODSTATE, cbReadWritten),
2022 SSMFIELD_ENTRY(SHCLCLIENTPODSTATE, tsLastReadWrittenMs),
2023 SSMFIELD_ENTRY_TERM()
2024};
2025
2026/**
2027 * SSM descriptor table for the SHCLCLIENTURISTATE structure.
2028 */
2029static SSMFIELD const s_aShClSSMClientTransferState[] =
2030{
2031 SSMFIELD_ENTRY(SHCLCLIENTTRANSFERSTATE, enmTransferDir),
2032 SSMFIELD_ENTRY_TERM()
2033};
2034
2035/**
2036 * SSM descriptor table for the header of the SHCLCLIENTMSG structure.
2037 * The actual message parameters will be serialized separately.
2038 */
2039static SSMFIELD const s_aShClSSMClientMsgHdr[] =
2040{
2041 SSMFIELD_ENTRY(SHCLCLIENTMSG, uMsg),
2042 SSMFIELD_ENTRY(SHCLCLIENTMSG, cParms),
2043 SSMFIELD_ENTRY_TERM()
2044};
2045
2046/**
2047 * SSM descriptor table for the VBOXSHCLMSGCTX structure.
2048 */
2049static SSMFIELD const s_aShClSSMClientMsgCtx[] =
2050{
2051 SSMFIELD_ENTRY(SHCLMSGCTX, uContextID),
2052 SSMFIELD_ENTRY_TERM()
2053};
2054#endif /* !UNIT_TEST */
2055
2056static DECLCALLBACK(int) svcSaveState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
2057{
2058 LogFlowFuncEnter();
2059
2060#ifndef UNIT_TEST
2061 /*
2062 * When the state will be restored, pending requests will be reissued
2063 * by VMMDev. The service therefore must save state as if there were no
2064 * pending request.
2065 * Pending requests, if any, will be completed in svcDisconnect.
2066 */
2067 RT_NOREF(u32ClientID);
2068 LogFunc(("u32ClientID=%RU32\n", u32ClientID));
2069
2070 PSHCLCLIENT pClient = (PSHCLCLIENT)pvClient;
2071 AssertPtr(pClient);
2072
2073 /* Write Shared Clipboard saved state version. */
2074 SSMR3PutU32(pSSM, VBOX_SHCL_SSM_VER_LATEST);
2075
2076 int rc = SSMR3PutStructEx(pSSM, &pClient->State, sizeof(pClient->State), 0 /*fFlags*/, &s_aShClSSMClientState[0], NULL);
2077 AssertRCReturn(rc, rc);
2078
2079 rc = SSMR3PutStructEx(pSSM, &pClient->State.POD, sizeof(pClient->State.POD), 0 /*fFlags*/, &s_aShClSSMClientPODState[0], NULL);
2080 AssertRCReturn(rc, rc);
2081
2082 rc = SSMR3PutStructEx(pSSM, &pClient->State.Transfers, sizeof(pClient->State.Transfers), 0 /*fFlags*/, &s_aShClSSMClientTransferState[0], NULL);
2083 AssertRCReturn(rc, rc);
2084
2085 /* Serialize the client's internal message queue. */
2086 rc = SSMR3PutU64(pSSM, (uint64_t)pClient->queueMsg.size());
2087 AssertRCReturn(rc, rc);
2088
2089 for (size_t i = 0; i < pClient->queueMsg.size(); i++)
2090 {
2091 PSHCLCLIENTMSG pMsg = pClient->queueMsg.at(i);
2092 AssertPtr(pMsg);
2093
2094 rc = SSMR3PutStructEx(pSSM, pMsg, sizeof(SHCLCLIENTMSG), 0 /*fFlags*/, &s_aShClSSMClientMsgHdr[0], NULL);
2095 AssertRCReturn(rc, rc);
2096
2097 rc = SSMR3PutStructEx(pSSM, &pMsg->Ctx, sizeof(SHCLMSGCTX), 0 /*fFlags*/, &s_aShClSSMClientMsgCtx[0], NULL);
2098 AssertRCReturn(rc, rc);
2099
2100 for (uint32_t p = 0; p < pMsg->cParms; p++)
2101 {
2102 rc = HGCMSvcSSMR3Put(&pMsg->paParms[p], pSSM);
2103 AssertRCReturn(rc, rc);
2104 }
2105 }
2106
2107#else /* UNIT_TEST */
2108 RT_NOREF3(u32ClientID, pvClient, pSSM);
2109#endif /* UNIT_TEST */
2110 return VINF_SUCCESS;
2111}
2112
2113#ifndef UNIT_TEST
2114static int svcLoadStateV0(uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion)
2115{
2116 RT_NOREF(u32ClientID, pvClient, pSSM, uVersion);
2117
2118 uint32_t uMarker;
2119 int rc = SSMR3GetU32(pSSM, &uMarker); /* Begin marker. */
2120 AssertRC(rc);
2121 Assert(uMarker == UINT32_C(0x19200102) /* SSMR3STRUCT_BEGIN */);
2122
2123 rc = SSMR3Skip(pSSM, sizeof(uint32_t)); /* Client ID */
2124 AssertRCReturn(rc, rc);
2125
2126 bool fValue;
2127 rc = SSMR3GetBool(pSSM, &fValue); /* fHostMsgQuit */
2128 AssertRCReturn(rc, rc);
2129
2130 rc = SSMR3GetBool(pSSM, &fValue); /* fHostMsgReadData */
2131 AssertRCReturn(rc, rc);
2132
2133 rc = SSMR3GetBool(pSSM, &fValue); /* fHostMsgFormats */
2134 AssertRCReturn(rc, rc);
2135
2136 uint32_t fFormats;
2137 rc = SSMR3GetU32(pSSM, &fFormats); /* u32RequestedFormat */
2138 AssertRCReturn(rc, rc);
2139
2140 rc = SSMR3GetU32(pSSM, &uMarker); /* End marker. */
2141 AssertRCReturn(rc, rc);
2142 Assert(uMarker == UINT32_C(0x19920406) /* SSMR3STRUCT_END */);
2143
2144 return VINF_SUCCESS;
2145}
2146#endif /* UNIT_TEST */
2147
2148static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion)
2149{
2150 LogFlowFuncEnter();
2151
2152#ifndef UNIT_TEST
2153
2154 RT_NOREF(u32ClientID, uVersion);
2155
2156 PSHCLCLIENT pClient = (PSHCLCLIENT)pvClient;
2157 AssertPtr(pClient);
2158
2159 /* Restore the client data. */
2160 uint32_t lenOrVer;
2161 int rc = SSMR3GetU32(pSSM, &lenOrVer);
2162 AssertRCReturn(rc, rc);
2163
2164 LogFunc(("u32ClientID=%RU32, lenOrVer=%#RX64\n", u32ClientID, lenOrVer));
2165
2166 if (lenOrVer == VBOX_SHCL_SSM_VER_0)
2167 {
2168 return svcLoadStateV0(u32ClientID, pvClient, pSSM, uVersion);
2169 }
2170 else if (lenOrVer >= VBOX_SHCL_SSM_VER_1)
2171 {
2172 if (lenOrVer >= VBOX_SHCL_SSM_VER_2)
2173 {
2174 rc = SSMR3GetStructEx(pSSM, &pClient->State, sizeof(pClient->State), 0 /* fFlags */,
2175 &s_aShClSSMClientState[0], NULL);
2176 AssertRCReturn(rc, rc);
2177
2178 rc = SSMR3GetStructEx(pSSM, &pClient->State.POD, sizeof(pClient->State.POD), 0 /* fFlags */,
2179 &s_aShClSSMClientPODState[0], NULL);
2180 AssertRCReturn(rc, rc);
2181 }
2182 else /** @todo Remove this block after 6.1 RC; don't annoy team members with broken saved states. */
2183 {
2184 rc = SSMR3Skip(pSSM, sizeof(uint32_t)); /* Begin marker */
2185 AssertRC(rc);
2186
2187 rc = SSMR3GetU64(pSSM, &pClient->State.fGuestFeatures0);
2188 AssertRC(rc);
2189
2190 rc = SSMR3GetU64(pSSM, &pClient->State.fGuestFeatures1);
2191 AssertRC(rc);
2192
2193 rc = SSMR3GetU32(pSSM, &pClient->State.cbChunkSize);
2194 AssertRC(rc);
2195
2196 rc = SSMR3GetU32(pSSM, (uint32_t *)&pClient->State.enmSource);
2197 AssertRC(rc);
2198
2199 rc = SSMR3Skip(pSSM, sizeof(uint32_t)); /* End marker */
2200 AssertRC(rc);
2201 }
2202
2203 rc = SSMR3GetStructEx(pSSM, &pClient->State.Transfers, sizeof(pClient->State.Transfers), 0 /* fFlags */,
2204 &s_aShClSSMClientTransferState[0], NULL);
2205 AssertRCReturn(rc, rc);
2206
2207 /* Load the client's internal message queue. */
2208 uint64_t cMsg;
2209 rc = SSMR3GetU64(pSSM, &cMsg);
2210 AssertRCReturn(rc, rc);
2211
2212 for (uint64_t i = 0; i < cMsg; i++)
2213 {
2214 PSHCLCLIENTMSG pMsg = (PSHCLCLIENTMSG)RTMemAlloc(sizeof(SHCLCLIENTMSG));
2215 AssertPtrReturn(pMsg, VERR_NO_MEMORY);
2216
2217 rc = SSMR3GetStructEx(pSSM, pMsg, sizeof(SHCLCLIENTMSG), 0 /*fFlags*/, &s_aShClSSMClientMsgHdr[0], NULL);
2218 AssertRCReturn(rc, rc);
2219
2220 rc = SSMR3GetStructEx(pSSM, &pMsg->Ctx, sizeof(SHCLMSGCTX), 0 /*fFlags*/, &s_aShClSSMClientMsgCtx[0], NULL);
2221 AssertRCReturn(rc, rc);
2222
2223 pMsg->paParms = (PVBOXHGCMSVCPARM)RTMemAllocZ(sizeof(VBOXHGCMSVCPARM) * pMsg->cParms);
2224 AssertPtrReturn(pMsg->paParms, VERR_NO_MEMORY);
2225
2226 for (uint32_t p = 0; p < pMsg->cParms; p++)
2227 {
2228 rc = HGCMSvcSSMR3Get(&pMsg->paParms[p], pSSM);
2229 AssertRCReturn(rc, rc);
2230 }
2231
2232 rc = shclSvcMsgAdd(pClient, pMsg, true /* fAppend */);
2233 AssertRCReturn(rc, rc);
2234 }
2235 }
2236 else
2237 {
2238 LogRel(("Shared Clipboard: Unknown saved state version (%#x)\n", lenOrVer));
2239 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2240 }
2241
2242 /* Actual host data are to be reported to guest (SYNC). */
2243 ShClSvcImplSync(pClient);
2244
2245#else /* UNIT_TEST */
2246 RT_NOREF(u32ClientID, pvClient, pSSM, uVersion);
2247#endif /* UNIT_TEST */
2248 return VINF_SUCCESS;
2249}
2250
2251static DECLCALLBACK(int) extCallback(uint32_t u32Function, uint32_t u32Format, void *pvData, uint32_t cbData)
2252{
2253 RT_NOREF(pvData, cbData);
2254
2255 LogFlowFunc(("u32Function=%RU32\n", u32Function));
2256
2257 int rc = VINF_SUCCESS;
2258
2259 /* Figure out if the client in charge for the service extension still is connected. */
2260 ClipboardClientMap::const_iterator itClient = g_mapClients.find(g_ExtState.uClientID);
2261 if (itClient != g_mapClients.end())
2262 {
2263 PSHCLCLIENT pClient = itClient->second;
2264 AssertPtr(pClient);
2265
2266 switch (u32Function)
2267 {
2268 /* The service extension announces formats to the guest. */
2269 case VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE:
2270 {
2271 LogFlowFunc(("VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE: g_ExtState.fReadingData=%RTbool\n", g_ExtState.fReadingData));
2272 if (g_ExtState.fReadingData)
2273 {
2274 g_ExtState.fDelayedAnnouncement = true;
2275 g_ExtState.uDelayedFormats = u32Format;
2276 }
2277 else if (u32Format != VBOX_SHCL_FMT_NONE)
2278 {
2279 SHCLFORMATDATA formatData;
2280 RT_ZERO(formatData);
2281
2282 formatData.uFormats = u32Format;
2283
2284 rc = ShClSvcFormatsReport(pClient, &formatData);
2285 }
2286
2287 break;
2288 }
2289
2290 /* The service extension wants read data from the guest. */
2291 case VBOX_CLIPBOARD_EXT_FN_DATA_READ:
2292 {
2293 SHCLDATAREQ dataReq;
2294 RT_ZERO(dataReq);
2295
2296 dataReq.uFmt = u32Format;
2297 dataReq.cbSize = _64K; /** @todo Make this more dynamic. */
2298
2299 rc = ShClSvcDataReadRequest(pClient, &dataReq, NULL /* puEvent */);
2300 break;
2301 }
2302
2303 default:
2304 /* Just skip other messages. */
2305 break;
2306 }
2307 }
2308 else
2309 rc = VERR_NOT_FOUND;
2310
2311 LogFlowFuncLeaveRC(rc);
2312 return rc;
2313}
2314
2315static DECLCALLBACK(int) svcRegisterExtension(void *, PFNHGCMSVCEXT pfnExtension, void *pvExtension)
2316{
2317 LogFlowFunc(("pfnExtension=%p\n", pfnExtension));
2318
2319 SHCLEXTPARMS parms;
2320 RT_ZERO(parms);
2321
2322 if (pfnExtension)
2323 {
2324 /* Install extension. */
2325 g_ExtState.pfnExtension = pfnExtension;
2326 g_ExtState.pvExtension = pvExtension;
2327
2328 parms.u.pfnCallback = extCallback;
2329
2330 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof(parms));
2331 }
2332 else
2333 {
2334 if (g_ExtState.pfnExtension)
2335 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof(parms));
2336
2337 /* Uninstall extension. */
2338 g_ExtState.pfnExtension = NULL;
2339 g_ExtState.pvExtension = NULL;
2340 }
2341
2342 return VINF_SUCCESS;
2343}
2344
2345extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
2346{
2347 int rc = VINF_SUCCESS;
2348
2349 LogFlowFunc(("pTable=%p\n", pTable));
2350
2351 if (!VALID_PTR(pTable))
2352 {
2353 rc = VERR_INVALID_PARAMETER;
2354 }
2355 else
2356 {
2357 LogFunc(("pTable->cbSize = %d, ptable->u32Version = 0x%08X\n", pTable->cbSize, pTable->u32Version));
2358
2359 if ( pTable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
2360 || pTable->u32Version != VBOX_HGCM_SVC_VERSION)
2361 {
2362 rc = VERR_VERSION_MISMATCH;
2363 }
2364 else
2365 {
2366 g_pHelpers = pTable->pHelpers;
2367
2368 pTable->cbClient = sizeof(SHCLCLIENT);
2369
2370 pTable->pfnUnload = svcUnload;
2371 pTable->pfnConnect = svcConnect;
2372 pTable->pfnDisconnect = svcDisconnect;
2373 pTable->pfnCall = svcCall;
2374 pTable->pfnHostCall = svcHostCall;
2375 pTable->pfnSaveState = svcSaveState;
2376 pTable->pfnLoadState = svcLoadState;
2377 pTable->pfnRegisterExtension = svcRegisterExtension;
2378 pTable->pfnNotify = NULL;
2379 pTable->pvService = NULL;
2380
2381 /* Service specific initialization. */
2382 rc = svcInit();
2383 }
2384 }
2385
2386 LogFlowFunc(("Returning %Rrc\n", rc));
2387 return rc;
2388}
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