1
/*********************************************************
2
* Copyright (C) 2007 VMware, Inc. All rights reserved.
4
* This program is free software; you can redistribute it and/or modify it
5
* under the terms of the GNU Lesser General Public License as published
6
* by the Free Software Foundation version 2.1 and no later version.
8
* This program is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10
* or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
11
* License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program; if not, write to the Free Software Foundation, Inc.,
15
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17
*********************************************************/
22
* Guest-host integration functions.
29
#include "ghIntegration.h"
30
#include "ghIntegrationInt.h"
31
#include "guestCaps.h"
32
#include "guestrpc/ghiGetBinaryHandlers.h"
33
#include "guestrpc/ghiProtocolHandler.h"
34
#include "guest_msg_def.h"
39
#include "unityCommon.h"
47
static Bool GHITcloGetBinaryInfo(char const **result, size_t *resultLen,
48
const char *name, const char *args,
49
size_t argsSize, void *clientData);
50
static Bool GHITcloGetBinaryHandlers(RpcInData *data);
51
static Bool GHITcloGetStartMenuItem(char const **result,
57
static Bool GHITcloOpenStartMenu(char const **result,
63
static Bool GHITcloCloseStartMenu(char const **result,
69
static Bool GHITcloShellOpen(char const **result,
75
static Bool GHITcloShellAction(char const **result,
81
static Bool GHITcloSetGuestHandler(RpcInData *data);
82
static Bool GHITcloRestoreDefaultGuestHandler(RpcInData *data);
84
static Bool GHIUpdateHost(GHIProtocolHandlerList *handlers);
87
static GHIPlatform *ghiPlatformData;
89
DblLnkLst_Links launchMenu;
92
*----------------------------------------------------------------------------
96
* Determine whether this guest supports guest-host integration.
99
* TRUE if the guest supports guest-host integration
105
*----------------------------------------------------------------------------
109
GHI_IsSupported(void)
111
return GHIPlatformIsSupported();
116
*-----------------------------------------------------------------------------
118
* GHI_RegisterCaps --
120
* Called by the application (VMwareUser) to allow the GHI subsystem to
121
* register its capabilities.
129
*-----------------------------------------------------------------------------
133
GHI_RegisterCaps(void)
135
/* Register guest platform specific capabilities. */
136
GHIPlatformRegisterCaps(ghiPlatformData);
141
*-----------------------------------------------------------------------------
143
* GHI_UnregisterCaps --
145
* Called by the application (VMwareUser) to allow the GHI subsystem to
146
* unregister its capabilities.
154
*-----------------------------------------------------------------------------
158
GHI_UnregisterCaps(void)
160
/* Unregister guest platform specific capabilities. */
161
GHIPlatformUnregisterCaps(ghiPlatformData);
166
*-----------------------------------------------------------------------------
170
* One time initialization stuff.
176
* May register with the tools poll loop.
178
*-----------------------------------------------------------------------------
182
GHI_Init(VMU_ControllerCB *vmuControllerCB, // IN
185
Debug("%s\n", __FUNCTION__);
187
DblLnkLst_Init(&launchMenu);
189
ghiPlatformData = GHIPlatformInit(vmuControllerCB, ctx);
194
*-----------------------------------------------------------------------------
206
*-----------------------------------------------------------------------------
212
GHIPlatformCleanup(ghiPlatformData);
213
ghiPlatformData = NULL;
218
*-----------------------------------------------------------------------------
220
* GHI_InitBackdoor --
222
* One time initialization stuff for the backdoor.
230
*-----------------------------------------------------------------------------
234
GHI_InitBackdoor(struct RpcIn *rpcIn) // IN
237
* Only register the callback if the guest is capable of supporting GHI.
238
* This way, if the VMX/UI sends us a GHI request on a non-supported platform
239
* (for whatever reason), we will reply with 'command not supported'.
242
if (GHI_IsSupported()) {
243
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_GET_BINARY_INFO,
244
GHITcloGetBinaryInfo, NULL);
245
RpcIn_RegisterCallbackEx(rpcIn, UNITY_RPC_GET_BINARY_HANDLERS,
246
GHITcloGetBinaryHandlers, NULL);
247
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_OPEN_LAUNCHMENU,
248
GHITcloOpenStartMenu, NULL);
249
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_GET_LAUNCHMENU_ITEM,
250
GHITcloGetStartMenuItem, NULL);
251
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_CLOSE_LAUNCHMENU,
252
GHITcloCloseStartMenu, NULL);
253
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_SHELL_OPEN,
254
GHITcloShellOpen, NULL);
255
RpcIn_RegisterCallback(rpcIn, GHI_RPC_GUEST_SHELL_ACTION,
256
GHITcloShellAction, NULL);
257
RpcIn_RegisterCallbackEx(rpcIn, GHI_RPC_SET_GUEST_HANDLER,
258
GHITcloSetGuestHandler, NULL);
259
RpcIn_RegisterCallbackEx(rpcIn, GHI_RPC_RESTORE_DEFAULT_GUEST_HANDLER,
260
GHITcloRestoreDefaultGuestHandler, NULL);
266
*-----------------------------------------------------------------------------
270
* Collects all the desired guest/host integration mapping details for
271
* URL Protocol handling and sending an RPC to the host with the collected
272
* details. Also initializes the global application -> filetype list.
278
* Updates the global application -> filetype list.
280
*-----------------------------------------------------------------------------
286
GHIProtocolHandlerList protocolHandlers;
288
/* Get Protocol Handler information. */
289
protocolHandlers.handlers.handlers_len = 0;
290
protocolHandlers.handlers.handlers_val = NULL;
292
if (!GHIPlatformGetProtocolHandlers(ghiPlatformData, &protocolHandlers)) {
293
Debug("Failed to get protocol handler info.\n");
295
if (!GHIUpdateHost(&protocolHandlers)) {
296
Debug("Failed to update the host.\n");
300
VMX_XDR_FREE(xdr_GHIProtocolHandlerList, &protocolHandlers);
304
AppUtil_BuildGlobalApplicationList();
307
Debug("Exited Guest/Host Integration Gather.\n");
312
*----------------------------------------------------------------------------
314
* GHITcloGetBinaryInfo --
316
* RPC handler for 'unity.get.binary.info'. Get required binary info
317
* and send it back to the VMX.
320
* TRUE if everything is successful.
326
*----------------------------------------------------------------------------
330
GHITcloGetBinaryInfo(char const **result, // OUT
331
size_t *resultLen, // OUT
332
const char *name, // IN
333
const char *args, // IN
334
size_t argsSize, // ignored
335
void *clientData) // ignored
338
char *binaryPathUtf8;
339
DynBuf *buf = &gTcloUpdate;
340
unsigned int index = 0;
343
Debug("%s name:%s args:'%s'\n", __FUNCTION__, name, args);
345
/* Skip the leading space. */
348
/* The binary path provided by the VMX is in UTF8. */
349
binaryPathUtf8 = StrUtil_GetNextToken(&index, args, "");
351
if (!binaryPathUtf8) {
352
Debug("%s: Invalid RPC arguments.\n", __FUNCTION__);
353
ret = RpcIn_SetRetVals(result, resultLen,
354
"Invalid arguments. Expected \"binary_path\"",
359
DynBuf_SetSize(buf, 0);
360
if (!GHIPlatformGetBinaryInfo(ghiPlatformData, binaryPathUtf8, buf)) {
361
Debug("%s: Could not get binary info.\n", __FUNCTION__);
362
ret = RpcIn_SetRetVals(result, resultLen,
363
"Could not get binary info",
369
* Write the final result into the result out parameters and return!
371
*result = (char *)DynBuf_Get(buf);
372
*resultLen = DynBuf_GetSize(buf);
375
free(binaryPathUtf8);
381
*----------------------------------------------------------------------------
383
* GHITcloGetBinaryHandlers --
385
* RPC handler for 'unity.get.binary.handlers'. Get filetypes supported
386
* by the binary and send it back to the VMX.
389
* TRUE if everything is successful.
395
*----------------------------------------------------------------------------
399
GHITcloGetBinaryHandlers(RpcInData *data) // IN/OUT
401
char *binaryPathUtf8;
403
unsigned int index = 0;
406
Debug("%s name:%s args:'%s'\n", __FUNCTION__, data->name, data->args);
408
/* Skip the leading space. */
411
/* The binary path provided by the VMX is in UTF8. */
412
binaryPathUtf8 = StrUtil_GetNextToken(&index, data->args, "");
414
if (!binaryPathUtf8) {
415
Debug("%s: Invalid RPC arguments.\n", __FUNCTION__);
416
ret = RPCIN_SETRETVALS(data, "Invalid arguments. Expected \"binary_path\"", FALSE);
420
DynXdr_Create(&xdrs);
421
if (!GHIPlatformGetBinaryHandlers(ghiPlatformData, binaryPathUtf8, &xdrs)) {
422
Debug("%s: Could not get binary filetypes.\n", __FUNCTION__);
423
ret = RPCIN_SETRETVALS(data, "Could not get binary filetypes", FALSE);
424
DynXdr_Destroy(&xdrs, FALSE);
429
* Write the final result into the result out parameters and return!
431
data->result = DynXdr_Get(&xdrs);
432
data->resultLen = xdr_getpos(&xdrs);
433
data->freeResult = TRUE;
437
* Destroy the XDR structure but leave the data buffer alone since it will be
438
* freed by the RpcIn layer.
440
DynXdr_Destroy(&xdrs, FALSE);
442
free(binaryPathUtf8);
448
*----------------------------------------------------------------------------
450
* GHITcloOpenStartMenu --
452
* RPC handler for 'unity.launchmenu.open'. Get the start menu sub-tree
453
* for a given item, save it in the array so it can be accessed
454
* later when the VMX needs to iterate over the items. Return the count
455
* of the items in the sub-tree and a handle to this sub-tree. The handle
456
* will be used by the VMX to iterate over the sub-items.
459
* TRUE if everything is successful.
465
*----------------------------------------------------------------------------
469
GHITcloOpenStartMenu(char const **result, // OUT
470
size_t *resultLen, // OUT
471
const char *name, // IN
472
const char *args, // IN
473
size_t argsSize, // ignored
474
void *clientData) // ignored
476
char *rootUtf8 = NULL;
477
DynBuf *buf = &gTcloUpdate;
481
Debug("%s name:%s args:'%s'\n", __FUNCTION__, name, args);
483
/* Skip the leading space. */
486
/* The start menu root provided by the VMX is in UTF8. */
487
rootUtf8 = StrUtil_GetNextToken(&index, args, "");
490
Debug("%s: Invalid RPC arguments.\n", __FUNCTION__);
491
ret = RpcIn_SetRetVals(result, resultLen,
492
"Invalid arguments. Expected \"root\"",
497
DynBuf_SetSize(buf, 0);
498
if (!GHIPlatformOpenStartMenuTree(ghiPlatformData, rootUtf8, buf)) {
499
Debug("%s: Could not open start menu.\n", __FUNCTION__);
500
ret = RpcIn_SetRetVals(result, resultLen,
501
"Could not get start menu count",
507
* Write the final result into the result out parameters and return!
509
*result = (char *)DynBuf_Get(buf);
510
*resultLen = DynBuf_GetSize(buf);
519
*----------------------------------------------------------------------------
521
* GHITcloGetStartMenuItem --
523
* RPC handler for 'unity.launchmenu.next'. Get the start menu item
524
* at the given index for the tree with a given handle.
525
* If there's no item at the given index, return FALSE.
528
* TRUE if the item was found.
529
* FALSE otherwise (i.e. if the VMX provides a wrong handle or if there's
535
*----------------------------------------------------------------------------
539
GHITcloGetStartMenuItem(char const **result, // OUT
540
size_t *resultLen, // OUT
541
const char *name, // IN
542
const char *args, // IN
543
size_t argsSize, // ignored
544
void *clientData) // ignored
546
DynBuf *buf = &gTcloUpdate;
549
uint32 itemIndex = 0;
552
Debug("%s name:%s args:'%s'\n", __FUNCTION__, name, args);
554
/* Parse the handle of the menu tree that VMX wants. */
555
if (!StrUtil_GetNextUintToken(&handle, &index, args, " ")) {
556
Debug("%s: Invalid RPC arguments.\n", __FUNCTION__);
557
return RpcIn_SetRetVals(result, resultLen,
558
"Invalid arguments. Expected handle index",
562
/* The index of the menu item to be send back. */
563
if (!StrUtil_GetNextUintToken(&itemIndex, &index, args, " ")) {
564
Debug("%s: Invalid RPC arguments.\n", __FUNCTION__);
565
return RpcIn_SetRetVals(result, resultLen,
566
"Invalid arguments. Expected handle index",
570
DynBuf_SetSize(buf, 0);
571
if (!GHIPlatformGetStartMenuItem(ghiPlatformData, handle, itemIndex, buf)) {
572
Debug("%s: Could not get start menu item.\n", __FUNCTION__);
573
return RpcIn_SetRetVals(result, resultLen,
574
"Could not get start menu item",
579
* Write the final result into the result out parameters and return!
581
*result = (char *)DynBuf_Get(buf);
582
*resultLen = DynBuf_GetSize(buf);
589
*----------------------------------------------------------------------------
591
* GHITcloCloseStartMenu --
593
* RPC handler for 'unity.launchmenu.close'. The VMX is done with this
594
* particular start menu tree. Free all memory and cleanup.
600
* Memory allocated when the start menu tree was opened is finally freed.
602
*----------------------------------------------------------------------------
606
GHITcloCloseStartMenu(char const **result, // OUT
607
size_t *resultLen, // OUT
608
const char *name, // IN
609
const char *args, // IN
610
size_t argsSize, // ignored
611
void *clientData) // ignored
616
Debug("%s name:%s args:'%s'\n", __FUNCTION__, name, args);
618
/* Parse the handle of the menu tree that VMX wants. */
619
if (!StrUtil_GetNextIntToken(&handle, &index, args, " ")) {
620
Debug("%s: Invalid RPC arguments.\n", __FUNCTION__);
621
return RpcIn_SetRetVals(result, resultLen,
622
"Invalid arguments. Expected handle",
626
GHIPlatformCloseStartMenuTree(ghiPlatformData, handle);
628
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
633
*----------------------------------------------------------------------------
635
* GHITcloShellOpen --
637
* RPC handler for 'unity.shell.open'. Open the specified file with the
638
* default shell handler. Note that the file path may be either a URI
639
* (originated with Tools >= NNNNN), or a regular path (originated with
643
* TRUE if everything is successful.
649
*----------------------------------------------------------------------------
653
GHITcloShellOpen(char const **result, // OUT
654
size_t *resultLen, // OUT
655
const char *name, // IN
656
const char *args, // IN
657
size_t argsSize, // ignored
658
void *clientData) // ignored
660
char *fileUtf8 = NULL;
662
unsigned int index = 0;
664
Debug("%s: name: '%s', args: '%s'\n", __FUNCTION__, name, args);
666
/* Skip the leading space. */
669
/* The file path provided by the VMX is in UTF8. */
670
fileUtf8 = StrUtil_GetNextToken(&index, args, "");
673
Debug("%s: Invalid RPC arguments.\n", __FUNCTION__);
674
return RpcIn_SetRetVals(result, resultLen,
675
"Invalid arguments. Expected file_name",
679
ret = GHIPlatformShellOpen(ghiPlatformData, fileUtf8);
683
Debug("%s: Could not perform the requested shell open action.\n", __FUNCTION__);
684
return RpcIn_SetRetVals(result, resultLen,
685
"Could not perform the requested shell open action.",
689
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
694
*----------------------------------------------------------------------------
696
* GHITcloShellAction --
698
* RPC handler for "ghi.guest.shell.action". The action command has three
699
* arguments: an action URI, a target URI, and an array of location URIs.
700
* Action URIs are in the form: "x-vmware-action://<verb>", where <verb> is
701
* the name of a specific action to perform.
702
* The target URI is a guest-specific URI that was previously given to the
703
* host (usually a path to an application to run). Note that this may be
704
* either a URI (new Tools) or a regular path (old Tools).
705
* The locations can be files or URLs. Files are typically specified as
706
* HGFS shared folder locations (see below), but can potentially use the
707
* "file://<path>" URIs as well.
708
* Each guest can specify the features it supports using capability flags:
710
* Capability Description
712
* GHI_CAP_CMD_SHELL_ACTION Guest allows 'ghi.guest.shell.action'.
713
* This encompasses this entire command
714
* and the rest of the capabilities.
716
* GHI_CAP_SHELL_ACTION_BROWSE Guest supports the 'browse' action verb,
717
* used to open a file browser window with
718
* a given set of locations.
720
* GHI_CAP_SHELL_ACTION_RUN Guest supports the 'run' action verb,
721
* used for running applications as well
722
* as opening file or URL locations.
724
* GHI_CAP_SHELL_LOCATION_HGFS Guest supports HGFS share location URIs:
725
* "x-vmware-share://<path>", where <path>
726
* specifies a shared folder name and an
727
* optional path within the shared folder.
730
* TRUE if everything is successful.
736
*----------------------------------------------------------------------------
740
GHITcloShellAction(char const **result, // OUT
741
size_t *resultLen, // OUT
742
const char *name, // IN
743
const char *args, // IN
744
size_t argsSize, // IN
745
void *clientData) // ignored
751
* Build an XDR Stream from the argument data which beings are args + 1
752
* since there is a space separator between the RPC name and the XDR serialization.
754
xdrmem_create(&xdrs, (char *) args + 1, argsSize - 1, XDR_DECODE);
756
ret = GHIPlatformShellAction(ghiPlatformData, &xdrs);
761
Debug("%s: Could not perform the requested shell action.\n", __FUNCTION__);
762
return RpcIn_SetRetVals(result, resultLen,
763
"Could not perform the requested shell action.",
767
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
772
*----------------------------------------------------------------------------
774
* GHITcloSetGuestHandler --
776
* RPC handler for 'ghi.guest.handler.set'. Changes the nominated handlerType
777
* to use the VMwareHostOpen proxy app to open files or URLs in the host.
780
* TRUE if everything is successful.
786
*----------------------------------------------------------------------------
790
GHITcloSetGuestHandler(RpcInData *data) // IN/OUT
795
Debug("%s name:%s args length: %"FMTSZ"u\n", __FUNCTION__, data->name, data->argsSize);
798
* Build an XDR Stream from the argument data which beings are args + 1
799
* since there is a space separator between the RPC name and the XDR serialization.
801
xdrmem_create(&xdrs, (char *) data->args + 1, data->argsSize - 1, XDR_DECODE);
802
ret = GHIPlatformSetGuestHandler(ghiPlatformData, &xdrs);
806
Debug("%s: Unable to set guest handler\n", __FUNCTION__);
807
ret = RPCIN_SETRETVALS(data, "Unable to set guest handler", FALSE);
811
* Write the final result into the result out parameters and return!
815
data->freeResult = FALSE;
824
*----------------------------------------------------------------------------
826
* GHITcloRestoreDefaultGuestHandler --
828
* RPC handler for 'ghi.guest.handler.restoreDefault'. Changes the nominated
829
* handlerType back to the value in use prior to any changes by tools.
832
* TRUE if everything is successful.
838
*----------------------------------------------------------------------------
842
GHITcloRestoreDefaultGuestHandler(RpcInData *data) // IN/OUT
847
Debug("%s name:%s args length: %"FMTSZ"u\n", __FUNCTION__, data->name, data->argsSize);
850
* Build an XDR Stream from the argument data which beings are args + 1
851
* since there is a space separator between the RPC name and the XDR serialization.
853
xdrmem_create(&xdrs, (char *) data->args + 1, data->argsSize - 1, XDR_DECODE);
854
ret = GHIPlatformRestoreDefaultGuestHandler(ghiPlatformData, &xdrs);
858
Debug("%s: Unable to restore guest handler\n", __FUNCTION__);
859
ret = RPCIN_SETRETVALS(data, "Unable to restore guest handler", FALSE);
863
* Write the final result into the result out parameters and return!
867
data->freeResult = FALSE;
876
*----------------------------------------------------------------------------
878
* GHILaunchMenuChangeRPC --
880
* Informs host that one or more Launch Menu changes have been detected.
889
*----------------------------------------------------------------------------
893
GHILaunchMenuChangeRPC(void)
895
if (!RpcOut_sendOne(NULL, NULL, GHI_RPC_LAUNCHMENU_CHANGE)) {
896
Debug("%s: could not send unity launchmenu change\n", __FUNCTION__);
905
*-----------------------------------------------------------------------------
909
* Update the host with new guest/host integration information.
912
* TRUE on success, FALSE on failure.
915
* VMDB is updated if the given value has changed.
917
*-----------------------------------------------------------------------------
921
GHIUpdateHost(GHIProtocolHandlerList *handlers) // IN: type specific information
923
/* +1 for the space separator */
924
char request[sizeof GHI_RPC_PROTOCOL_HANDLER_INFO + 1];
930
if (DynXdr_Create(&xdrs) == NULL) {
937
GHI_RPC_PROTOCOL_HANDLER_INFO);
939
/* Write preamble and serialized protocol handler info to XDR stream. */
940
if (!DynXdr_AppendRaw(&xdrs, request, strlen(request)) ||
941
!xdr_GHIProtocolHandlerList(&xdrs, handlers)) {
942
Debug("%s: could not serialize protocol handler info\n", __FUNCTION__);
943
DynXdr_Destroy(&xdrs, TRUE);
947
status = RpcOut_SendOneRaw(DynXdr_Get(&xdrs),
951
DynXdr_Destroy(&xdrs, TRUE);
954
Debug("%s: failed to update protocol handler information\n",