Esempio n. 1
0
static gboolean
ToolsCoreRpcCapReg(RpcInData *data)
{
   char *confPath = GuestApp_GetConfPath();
   gchar *msg;
   GArray *pcaps = NULL;
   ToolsServiceState *state = data->clientData;

   g_signal_emit_by_name(state->ctx.serviceObj,
                         TOOLS_CORE_SIG_CAPABILITIES,
                         &state->ctx,
                         TRUE,
                         &pcaps);

   if (pcaps != NULL) {
      ToolsCore_SetCapabilities(state->ctx.rpc, pcaps, TRUE);
      g_array_free(pcaps, TRUE);
   }

   /* Tell the host the location of the conf directory. */
   msg = g_strdup_printf("tools.capability.guest_conf_directory %s", confPath);
   if (!RpcChannel_Send(state->ctx.rpc, msg, strlen(msg) + 1, NULL, NULL)) {
      g_debug("Unable to register guest conf directory capability.\n");
   }
   g_free(msg);
   msg = NULL;

   /* Send the tools version to the VMX. */
   if (state->mainService) {
      uint32 version;
      char *result = NULL;
      size_t resultLen;
      gchar *toolsVersion;

#if defined(OPEN_VM_TOOLS)
      version = TOOLS_VERSION_UNMANAGED;
#else
      gboolean disableVersion;

      disableVersion = g_key_file_get_boolean(state->ctx.config,
                                              "vmtools",
                                              CONFNAME_DISABLETOOLSVERSION,
                                              NULL);
      version = disableVersion ? TOOLS_VERSION_UNMANAGED : TOOLS_VERSION_CURRENT;
#endif

      toolsVersion = g_strdup_printf("tools.set.version %u", version);

      if (!RpcChannel_Send(state->ctx.rpc, toolsVersion, strlen(toolsVersion) + 1,
                           &result, &resultLen)) {
         g_debug("Error setting tools version: %s.\n", result);
      }
      vm_free(result);
      g_free(toolsVersion);
   }

   state->capsRegistered = TRUE;
   free(confPath);
   return RPCIN_SETRETVALS(data, "", TRUE);
}
Esempio n. 2
0
Bool
VmBackup_SendEvent(const char *event,
                   const uint32 code,
                   const char *desc)
{
   Bool success;
   char *result;
   size_t resultLen;
   gchar *msg;

   ASSERT(gBackupState != NULL);

   g_debug("*** %s\n", __FUNCTION__);
   if (gBackupState->keepAlive != NULL) {
      g_source_destroy(gBackupState->keepAlive);
      g_source_unref(gBackupState->keepAlive);
   }

   msg = g_strdup_printf(VMBACKUP_PROTOCOL_EVENT_SET" %s %u %s", event, code, desc);
   success = RpcChannel_Send(gBackupState->ctx->rpc, msg, strlen(msg) + 1,
                             &result, &resultLen);

   if (!success) {
      g_warning("Failed to send event to the VMX: %s.\n", result);
   }

   gBackupState->keepAlive = g_timeout_source_new(VMBACKUP_KEEP_ALIVE_PERIOD / 2);
   VMTOOLSAPP_ATTACH_SOURCE(gBackupState->ctx,
                            gBackupState->keepAlive,
                            VmBackupKeepAliveCallback,
                            NULL,
                            NULL);
   return success;
}
Esempio n. 3
0
static Bool
VmBackupPrivSendMsg(gchar *msg,
                    char **result,
                    size_t *resultLen)
{
   Bool success;
   unsigned int oldLevel;

   ASSERT(gBackupState != NULL);

   g_debug("*** %s\n", __FUNCTION__);

   oldLevel = Iopl_Get();

   g_debug("Raising the IOPL, oldLevel=%u\n", oldLevel);
   if (iopl(3) < 0) {
      g_warning("Error raising the IOPL, %s\n", strerror(errno));
   }

   success = RpcChannel_Send(gBackupState->ctx->rpc, msg,
                             strlen(msg) + 1,
                             result, resultLen);

   if (iopl(oldLevel) < 0) {
      g_warning("Error restoring the IOPL, %s\n", strerror(errno));
   }

   return success;
}
Esempio n. 4
0
static void
ToolsCoreCheckReset(struct RpcChannel *chan,
                    gboolean success,
                    gpointer _state)
{
   ToolsServiceState *state = _state;

   ASSERT(state != NULL);

   if (success) {
      const gchar *app;
      gchar *msg;

      app = ToolsCore_GetTcloName(state);
      if (app == NULL) {
         app = state->name;
      }

      msg = g_strdup_printf("vmx.capability.unified_loop %s", app);
      if (!RpcChannel_Send(state->ctx.rpc, msg, strlen(msg) + 1, NULL, NULL)) {
         g_warning("VMX doesn't support the Tools unified loop.\n"
                   "Some functionality (like setting options) may not work.\n");
      }
      g_free(msg);

      /*
       * Log the Tools build number to the VMX log file. We don't really care
       * if sending the message fails.
       */
      msg = g_strdup_printf("log %s: Version: %s", app, BUILD_NUMBER);
      RpcChannel_Send(state->ctx.rpc, msg, strlen(msg) + 1, NULL, NULL);
      g_free(msg);

      g_signal_emit_by_name(state->ctx.serviceObj,
                            TOOLS_CORE_SIG_RESET,
                            &state->ctx);
   } else {
      VMTOOLSAPP_ERROR(&state->ctx, EXIT_FAILURE);
   }
}
Esempio n. 5
0
gboolean
RpcChannel_SendOneRaw(const char *data,
                      size_t dataLen,
                      char **result,
                      size_t *resultLen)
{
   RpcChannel *chan;
   gboolean status;

   status = FALSE;

   chan = RpcChannel_New();
   if (chan == NULL) {
      if (result != NULL) {
         *result = Util_SafeStrdup("RpcChannel: Unable to create "
                                   "the RpcChannel object");
         if (resultLen != NULL) {
            *resultLen = strlen(*result);
         }
      }
      goto sent;
   } else if (!RpcChannel_Start(chan)) {
      if (result != NULL) {
         *result = Util_SafeStrdup("RpcChannel: Unable to open the "
                                   "communication channel");
         if (resultLen != NULL) {
            *resultLen = strlen(*result);
         }
      }
      goto sent;
   } else if (!RpcChannel_Send(chan, data, dataLen, result, resultLen)) {
      /* We already have the description of the error */
      goto sent;
   }

   status = TRUE;

sent:
   Debug(LGPFX "Request %s: reqlen=%"FMTSZ"u, replyLen=%"FMTSZ"u\n",
         status ? "OK" : "FAILED", dataLen, resultLen ? *resultLen : 0);
   if (chan) {
      RpcChannel_Stop(chan);
      RpcChannel_Destroy(chan);
   }

   return status;
}
Esempio n. 6
0
static void
PowerOpsStateChangeDone(PowerOpState *state,
                        gboolean success)
{
   gchar *msg;

   g_debug("State change complete, success = %d.\n", success);

   /*
    * We execute the requested action if the script succeeded, or if the
    * same action was tried before but didn't finish due to a script failure.
    * See bug 168568 for discussion.
    */
   if (success || state->lastFailedStateChg == state->stateChgInProgress) {
      success = TRUE;
      state->lastFailedStateChg = GUESTOS_STATECHANGE_NONE;
   }

   if (!success) {
      state->lastFailedStateChg = state->stateChgInProgress;
   }

   /* Send the status message to the VMX. */
   msg = g_strdup_printf("tools.os.statechange.status %d %d",
                         success,
                         state->stateChgInProgress);
   if (!RpcChannel_Send(state->ctx->rpc, msg, strlen(msg) + 1, NULL, NULL)) {
      g_warning("Unable to send the status RPC.");
   }

   g_free(msg);

   /* Finally, perform the requested operation. */
   if (success) {
      if (state->stateChgInProgress == GUESTOS_STATECHANGE_REBOOT) {
         g_message("Initiating reboot.\n");
         System_Shutdown(TRUE);
      } else if (state->stateChgInProgress == GUESTOS_STATECHANGE_HALT) {
         g_message("Initiating halt.\n");
         System_Shutdown(FALSE);
      }
   }

   state->stateChgInProgress = GUESTOS_STATECHANGE_NONE;
}
Esempio n. 7
0
static void
ResolutionSetServerCapability(RpcChannel *chan,
                              unsigned int value)
{
   gchar *msg;

   if (!rpcChannelName) {
      g_debug("Channel name is null, RPC not sent.\n");
      return;
   }

   msg = g_strdup_printf("tools.capability.resolution_server %s %d",
                         rpcChannelName,
                         value);
   if (!RpcChannel_Send(chan, msg, strlen(msg), NULL, NULL)) {
      g_warning("%s: Unable to set tools.capability.resolution_server\n",
                __FUNCTION__);
   }
   g_free(msg);
}
Esempio n. 8
0
void
CopyPasteDnDWrapper::OnResetInternal()
{
   g_debug("%s: enter\n", __FUNCTION__);

   /*
    * Reset DnD/Copy/Paste only if vmx said we can. The reason is that
    * we may also get reset request from vmx when user is taking snapshot
    * or recording. If there is an ongoing DnD/copy/paste, we should not
    * reset here. For details please refer to bug 375928.
    */
   char *reply = NULL;
   size_t replyLen;
   ToolsAppCtx *ctx = GetToolsAppCtx();
   if (RpcChannel_Send(ctx->rpc, "dnd.is.active",
                       strlen("dnd.is.active"), &reply, &replyLen) &&
       (1 == atoi(reply))) {
      g_debug("%s: ignore reset while file transfer is busy.\n", __FUNCTION__);
      goto exit;
   }

   if (IsDnDRegistered()) {
      UnregisterDnD();
   }
   if (IsCPRegistered()) {
      UnregisterCP();
   }
   if (IsCPEnabled() && !IsCPRegistered()) {
      RegisterCP();
   }
   if (IsDnDEnabled() && !IsDnDRegistered()) {
      RegisterDnD();
   }
   if (!IsDnDRegistered() || !IsCPRegistered()) {
      g_debug("%s: unable to reset fully DnD %d CP %d!\n",
            __FUNCTION__, IsDnDRegistered(), IsCPRegistered());
   }

exit:
   free(reply);
}
Esempio n. 9
0
gboolean
ToolsCmd_SendRPC(const char *rpc,      // IN
                 size_t rpcLen,        // IN
                 char **result,        // OUT
                 size_t *resultLen)    // OUT
{
   char *lrpc = (char *) rpc;
   RpcChannel *chan = BackdoorChannel_New();
   gboolean ret = RpcChannel_Start(chan);

   if (!ret) {
      g_warning("Error starting RPC channel.");
      goto exit;
   }

   ret = RpcChannel_Send(chan, lrpc, rpcLen, result, resultLen);

exit:
   RpcChannel_Destroy(chan);
   return ret;
}
Esempio n. 10
0
int
CopyPasteDnDWrapper::GetDnDVersion()
{
   g_debug("%s: enter\n", __FUNCTION__);
   if (IsDnDRegistered()) {
      char *reply = NULL;
      size_t replyLen;

      ToolsAppCtx *ctx = GetToolsAppCtx();
      if (!RpcChannel_Send(ctx->rpc, QUERY_VMX_DND_VERSION,
                           strlen(QUERY_VMX_DND_VERSION), &reply, &replyLen)) {
         g_debug("%s: could not get VMX dnd "
               "version capability: %s\n", __FUNCTION__, reply ? reply : "NULL");
         m_dndVersion = 1;
      } else {
         m_dndVersion = atoi(reply);
      }
      free(reply);
   }
   g_debug("%s: got version %d\n", __FUNCTION__, m_dndVersion);
   return m_dndVersion;
}
Esempio n. 11
0
static GArray *
HgfsServerCapReg(gpointer src,
                 ToolsAppCtx *ctx,
                 gboolean set,
                 ToolsPluginData *plugin)
{
   gchar *msg;
   const char *appName = NULL;

   if (strcmp(ctx->name, VMTOOLS_GUEST_SERVICE) == 0) {
      appName = TOOLS_DAEMON_NAME;
   } else if (strcmp(ctx->name, VMTOOLS_USER_SERVICE) == 0) {
      appName = TOOLS_DND_NAME;
   } else {
      NOT_REACHED();
   }

   msg = g_strdup_printf("tools.capability.hgfs_server %s %d",
                         appName,
                         set ? 1 : 0);

   /*
    * Prior to WS55, the VMX did not know about the "hgfs_server"
    * capability. This doesn't mean that the HGFS server wasn't needed, it's
    * just that the capability was introduced in CS 225439 so that the VMX
    * could decide which HGFS server to communicate with.
    *
    * Long story short, we shouldn't care if this function fails.
    */
   if (ctx->rpc && !RpcChannel_Send(ctx->rpc, msg, strlen(msg) + 1, NULL, NULL)) {
      g_warning("Setting HGFS server capability failed!\n");
   }

   g_free(msg);
   return NULL;
}
void
ToolsDaemonTcloReportProgramCompleted(const char *requestName,    // IN
                                      VixError err,               // IN
                                      int exitCode,               // IN
                                      int64 pid,                  // IN
                                      void *clientData)           // IN
{
   Bool sentResult;
   ToolsAppCtx *ctx = clientData;
   gchar *msg = g_strdup_printf("%s %s %"FMT64"d %d %d %"FMT64"d",
                                VIX_BACKDOORCOMMAND_RUN_PROGRAM_DONE,
                                requestName,
                                err,
                                Err_Errno(),
                                exitCode,
                                (int64) pid);

   sentResult = RpcChannel_Send(ctx->rpc, msg, strlen(msg) + 1, NULL, NULL);
   g_free(msg);

   if (!sentResult) {
      Warning("Unable to send results from polling the result program.\n\n");
   }
} // ToolsDaemonTcloReportProgramCompleted
Esempio n. 13
0
void
CopyPasteDnDWrapper::OnCapReg(gboolean set)
{
   g_debug("%s: enter\n", __FUNCTION__);
   char *reply = NULL;
   size_t replyLen;
   const char *toolsDnDVersion = TOOLS_DND_VERSION_4;
   char *toolsCopyPasteVersion = NULL;
   int version;

   ToolsAppCtx *ctx = GetToolsAppCtx();
   if (ctx) {
      /*
       * First DnD.
       */
      if (!RpcChannel_Send(ctx->rpc, toolsDnDVersion, strlen(toolsDnDVersion),
                           NULL, NULL)) {
         g_debug("%s: could not set guest dnd version capability\n",
               __FUNCTION__);
         version = 1;
         SetDnDVersion(version);
      } else {
         char const *vmxDnDVersion = QUERY_VMX_DND_VERSION;

         if (!RpcChannel_Send(ctx->rpc, vmxDnDVersion,
                              strlen(vmxDnDVersion), &reply, &replyLen)) {
            g_debug("%s: could not get VMX dnd version capability, assuming v1\n",
                  __FUNCTION__);
            version = 1;
            SetDnDVersion(version);
         } else {
            int version = atoi(reply);
            ASSERT(version >= 1);
            SetDnDVersion(version);
            g_debug("%s: VMX is dnd version %d\n", __FUNCTION__, GetDnDVersion());
            if (version == 3) {
               /*
                * VMDB still has version 4 in it, which will cause a V3
                * host to fail. So, change to version 3. Since we don't
                * support any other version, we only do this for V3.
                */
               toolsDnDVersion = TOOLS_DND_VERSION_3;
               if (!RpcChannel_Send(ctx->rpc, toolsDnDVersion,
                                    strlen(toolsDnDVersion), NULL, NULL)) {

                  g_debug("%s: could not set VMX dnd version capability, assuming v1\n",
                           __FUNCTION__);
                  version = 1;
                  SetDnDVersion(version);
               }
            }
         }
         vm_free(reply);
         reply = NULL;
       }

      /*
       * Now CopyPaste.
       */

      toolsCopyPasteVersion = g_strdup_printf(TOOLS_COPYPASTE_VERSION" %d", 4);
      if (!RpcChannel_Send(ctx->rpc, toolsCopyPasteVersion,
                           strlen(toolsCopyPasteVersion),
                           NULL, NULL)) {
         g_debug("%s: could not set guest copypaste version capability\n",
               __FUNCTION__);
         version = 1;
         SetCPVersion(version);
      } else {
         char const *vmxCopyPasteVersion = QUERY_VMX_COPYPASTE_VERSION;

         if (!RpcChannel_Send(ctx->rpc, vmxCopyPasteVersion,
                              strlen(vmxCopyPasteVersion), &reply, &replyLen)) {
            g_debug("%s: could not get VMX copypaste version capability, assuming v1\n",
                  __FUNCTION__);
            version = 1;
            SetCPVersion(version);
         } else {
            version = atoi(reply);
            ASSERT(version >= 1);
            SetCPVersion(version);
            g_debug("%s: VMX is copypaste version %d\n", __FUNCTION__,
                  GetCPVersion());
            if (version == 3) {
               /*
                * VMDB still has version 4 in it, which will cause a V3
                * host to fail. So, change to version 3. Since we don't
                * support any other version, we only do this for V3.
                */
               g_free(toolsCopyPasteVersion);
               toolsCopyPasteVersion = g_strdup_printf(TOOLS_COPYPASTE_VERSION" %d", 3);
               if (!RpcChannel_Send(ctx->rpc, toolsCopyPasteVersion,
                                    strlen(toolsCopyPasteVersion), NULL, NULL)) {

                  g_debug("%s: could not set VMX copypaste version, assuming v1\n",
                           __FUNCTION__);
                  version = 1;
                  SetCPVersion(version);
               }
            }
         }
         vm_free(reply);
      }
      g_free(toolsCopyPasteVersion);
   }
}
DnDCPTransportGuestRpc::DnDCPTransportGuestRpc(RpcChannel *chan)
   : mRpcChannel(chan)
#else
DnDCPTransportGuestRpc::DnDCPTransportGuestRpc(void)
#endif
{
   for (int i = 0; i < TRANSPORT_INTERFACE_MAX; i++) {
      mCBCtx[i].transport = this;
      mCBCtx[i].type = (TransportInterfaceType)i;
   }
}


/**
 * Register a rpc callback to an interface.
 *
 * @param[in] rpc rpc which is listening to the message.
 * @param[in] type the interface type rpc is listening to.
 *
 * @return true on success, false otherwise.
 */

bool
DnDCPTransportGuestRpc::RegisterRpc(RpcBase *rpc,
                                    TransportInterfaceType type)
{
   if (mTables.GetRpc(type)) {
      LOG(0, ("%s: the type %d is already registered\n", __FUNCTION__, type));
      UnregisterRpc(type);
   }
   const char *cmdStr = (const char *)mTables.GetCmdStr(type);
   const char *disableStr = mTables.GetDisableStr(type);

   if (!cmdStr || !disableStr) {
      LOG(0, ("%s: can not find valid cmd for %d, cmdStr %s disableStr %s\n", __FUNCTION__, type, (cmdStr ? cmdStr : "NULL"), (disableStr ? disableStr : "NULL")));
      return false;
   }

   ASSERT(mCBCtx);
   ASSERT(type == TRANSPORT_GUEST_CONTROLLER_DND ||
          type == TRANSPORT_GUEST_CONTROLLER_CP ||
          type == TRANSPORT_GUEST_CONTROLLER_FT);
   LOG(4, ("%s: for %s\n", __FUNCTION__, cmdStr));

#ifdef VMX86_TOOLS
   ASSERT(mRpcChannel);
   mRpcChanCBList[type].name = cmdStr;
   mRpcChanCBList[type].callback = RecvMsgCB;
   mRpcChanCBList[type].clientData = &mCBCtx[type];
   mRpcChanCBList[type].xdrIn = NULL;
   mRpcChanCBList[type].xdrOut = NULL;
   mRpcChanCBList[type].xdrInSize = 0;
   RpcChannel_RegisterCallback(mRpcChannel, &mRpcChanCBList[type]);
#else
   GuestRpc_RegisterCommand(mTables.GetCmd(type), disableStr,
                            (const unsigned char *)cmdStr, RecvMsgCB, &mCBCtx[type]);
#endif
   mTables.SetRpc(type, rpc);
   return true;
}


/**
 * Unregister a rpc callback.
 *
 * @param[in] type the interface type rpc is listening to.
 *
 * @return true on success, false otherwise.
 */

bool
DnDCPTransportGuestRpc::UnregisterRpc(TransportInterfaceType type)
{
   if (!mTables.GetRpc(type)) {
      LOG(0, ("%s: the type %d is not registered\n", __FUNCTION__, type));
      return false;
   }
#ifdef VMX86_TOOLS
   ASSERT(mRpcChannel);
   RpcChannel_UnregisterCallback(mRpcChannel, &mRpcChanCBList[type]);
#else
   GuestRpc_UnregisterCommand(mTables.GetCmd(type));
#endif
   mTables.SetRpc(type, NULL);
   return true;
}


/**
 * Wrap the buffer into an rpc and send it to the peer.
 *
 * @param[ignored] destId destination address id
 * @param[in] type transport interface type
 * @param[in] data Payload buffer
 * @param[in] dataSize Payload buffer size
 *
 * @return true on success, false otherwise.
 */

bool
DnDCPTransportGuestRpc::SendPacket(uint32 destId,
                                   TransportInterfaceType type,
                                   const uint8 *msg,
                                   size_t length)
{
   char *rpc = NULL;
   size_t rpcSize = 0;
   size_t nrWritten = 0;
   const char *cmd = mTables.GetCmdStr(type);
   bool ret = true;

   if (!cmd) {
      LOG(0, ("%s: can not find valid cmd for %d\n", __FUNCTION__, type));
      return false;
   }
   rpcSize = strlen(cmd) + 1 + length;
   rpc = (char *)Util_SafeMalloc(rpcSize);
   nrWritten = Str_Sprintf(rpc, rpcSize, "%s ", cmd);

   if (length > 0) {
      ASSERT(nrWritten + length <= rpcSize);
      memcpy(rpc + nrWritten, msg, length);
   }

#ifdef VMX86_TOOLS
   ret = (TRUE == RpcChannel_Send(mRpcChannel, rpc, rpcSize, NULL, NULL));

   if (!ret) {
      LOG(0, ("%s: failed to send msg to host\n", __FUNCTION__));
   }

   free(rpc);
#else
   GuestRpc_SendWithTimeOut((const unsigned char *)TOOLS_DND_NAME,
                            (const unsigned char *)rpc, rpcSize,
                            GuestRpc_GenericCompletionRoutine, rpc,
                            DND_TIMEOUT);
#endif
   return ret;
}
Esempio n. 15
0
Bool
VmBackup_SendEvent(const char *event,
                   const uint32 code,
                   const char *desc)
{
   Bool success;
   char *result = NULL;
   size_t resultLen;
   gchar *msg;

   ASSERT(gBackupState != NULL);

   g_debug("*** %s\n", __FUNCTION__);
   if (gBackupState->keepAlive != NULL) {
      g_source_destroy(gBackupState->keepAlive);
      g_source_unref(gBackupState->keepAlive);
   }

   msg = g_strdup_printf(VMBACKUP_PROTOCOL_EVENT_SET" %s %u %s",
                         event, code, desc);
   g_debug("Sending vmbackup event: %s\n", msg);

#if defined(__linux__)
   if (gBackupState->needsPriv) {
      success = VmBackupPrivSendMsg(msg, &result, &resultLen);
   } else {
      success = RpcChannel_Send(gBackupState->ctx->rpc,
                                msg, strlen(msg) + 1,
                                &result, &resultLen);
      if (!success) {
         const char *privErr = "Guest is not privileged";
         if (resultLen > strlen(privErr) &&
             strncmp(result, privErr, strlen(privErr)) == 0) {
            g_debug("Failed to send event: %s\n", result);
            vm_free(result);

            /*
             * PR1444259:
             * Some hosts enforce privilege elevation for sending this
             * event, especially 5.5. This is Linux specific because
             * version 9.4.x on Linux only triggers host side check for
             * privilege elevation by sending iopl_elevation capability
             * to the host.
             */
            gBackupState->needsPriv = TRUE;

            g_debug("Sending event with priv: %s\n", msg);
            success = VmBackupPrivSendMsg(msg, &result, &resultLen);
         } else {
            gBackupState->needsPriv = FALSE;
         }
      }
   }
#else
   success = RpcChannel_Send(gBackupState->ctx->rpc,
                             msg, strlen(msg) + 1,
                             &result, &resultLen);
#endif

   if (!success) {
      g_warning("Failed to send vmbackup event: %s.\n", result);
   }
   vm_free(result);
   g_free(msg);

   gBackupState->keepAlive = g_timeout_source_new(VMBACKUP_KEEP_ALIVE_PERIOD / 2);
   VMTOOLSAPP_ATTACH_SOURCE(gBackupState->ctx,
                            gBackupState->keepAlive,
                            VmBackupKeepAliveCallback,
                            NULL,
                            NULL);
   return success;
}
Esempio n. 16
0
void
ToolsCore_SetCapabilities(RpcChannel *chan,
                          GArray *caps,
                          gboolean set)
{
   char *result;
   size_t resultLen;
   guint i;
   gchar *newcaps = NULL;

   for (i = 0; i < caps->len; i++) {
      gchar *tmp;
      ToolsAppCapability *cap =  &g_array_index(caps, ToolsAppCapability, i);
      switch (cap->type) {
      case TOOLS_CAP_OLD:
         result = NULL;
         tmp = g_strdup_printf("tools.capability.%s %u",
                               cap->name,
                               set ? cap->value : 0);
         if (!RpcChannel_Send(chan, tmp, strlen(tmp) + 1, &result, &resultLen)) {
            g_warning("Error sending capability %s: %s\n", cap->name, result);
         }
         vm_free(result);
         g_free(tmp);
         break;

      case TOOLS_CAP_OLD_NOVAL:
         /*
          * This is kind of weird, because of the way the VMX treats RPCs and
          * what is expected of these capabilities without arguments. For a
          * few details, see the comments in RpcOut_sendOne() (rpcout.c).
          * Basically, for the VMX handlers not to complain, we need to send the
          * RPC with the empty space at the end, and not consider the NULL
          * character when counting the bytes.
          */
         if (set) {
            tmp = g_strdup_printf("tools.capability.%s ", cap->name);
            if (!RpcChannel_Send(chan, tmp, strlen(tmp), &result, &resultLen)) {
               g_warning("Error sending capability %s: %s\n", cap->name, result);
            }
            vm_free(result);
            g_free(tmp);
         }
         break;

      case TOOLS_CAP_NEW:
         if (newcaps == NULL) {
            newcaps = g_strdup(GUEST_CAP_FEATURES);
         }
         tmp = g_strdup_printf("%s %d=%u",
                               newcaps,
                               cap->index,
                               set ? cap->value : 0);
         g_free(newcaps);
         newcaps = tmp;
         break;

      default:
         g_error("Invalid capability type: %d\n", cap->type);
      }
   }

   if (newcaps != NULL) {
      result = NULL;
      if (!RpcChannel_Send(chan, newcaps, strlen(newcaps) + 1, &result, &resultLen)) {
         g_warning("Error sending new-style capabilities: %s\n", result);
      }
      vm_free(result);
      g_free(newcaps);
   }
}