static gboolean
RpcInStart(RpcChannel *chan)
{
   gboolean ret = TRUE;
   BackdoorChannel *bdoor = chan->_private;

   if (bdoor->outStarted) {
      /* Already started. Make sure both channels are in sync and return. */
      ASSERT(bdoor->in == NULL || bdoor->inStarted);
      return ret;
   } else {
      ASSERT(bdoor->in == NULL || !bdoor->inStarted);
   }

   if (bdoor->in != NULL) {
      ret = RpcIn_start(bdoor->in, RPCIN_MAX_DELAY, RpcChannel_Error, chan);
   }
   if (ret) {
      ret = RpcOut_start(bdoor->out);
      if (!ret) {
         RpcIn_stop(bdoor->in);
      }
   }
   bdoor->inStarted = (bdoor->in != NULL);
   bdoor->outStarted = TRUE;
   return ret;
}
static gboolean
BkdoorChannelStart(RpcChannel *chan)
{
   gboolean ret = TRUE;
   BackdoorChannel *bdoor = chan->_private;

   ret = chan->in == NULL || chan->inStarted;
   if (ret) {
      ret = RpcOut_start(bdoor->out);
      if (!ret) {
         if (chan->inStarted) {
            RpcIn_stop(chan->in);
            chan->inStarted = FALSE;
         }
      }
   }
   chan->outStarted = ret;
   return ret;
}
static gboolean
BkdoorChannelSend(RpcChannel *chan,
                  char const *data,
                  size_t dataLen,
                  char **result,
                  size_t *resultLen)
{
   gboolean ret = FALSE;
   const char *reply;
   size_t replyLen;
   BackdoorChannel *bdoor = chan->_private;

   if (!chan->outStarted) {
      goto exit;
   }

   ret = RpcOut_send(bdoor->out, data, dataLen, &reply, &replyLen);

   /*
    * This is a hack to try to work around bug 393650 without having to revert
    * to the old behavior of opening and closing an RpcOut channel for every
    * outgoing message. The issue here is that it's possible for the code to
    * try to write to the channel when a "reset" has just happened. In these
    * cases, the current RpcOut channel is not valid anymore, and we'll get an
    * error. The RpcOut lib doesn't really reply with a useful error, but it
    * does have consistent error messages starting with "RpcOut:".
    *
    * So, if the error is one of those messages, restart the RpcOut channel and
    * try to send the message again. If this second attempt fails, then give up.
    *
    * This is not 100% break-proof: a reset can still occur after we open the
    * new channel and before we try to re-send the message. But that's a race
    * that we can't easily fix, and exists even in code that just uses the
    * RpcOut_SendOne() API. Also, if some host handler returns an error that
    * starts with "RpcOut:", it will trigger this; but I don't think we have
    * any such handlers.
    */
   if (!ret && reply != NULL && replyLen > sizeof "RpcOut: " &&
       g_str_has_prefix(reply, "RpcOut: ")) {
      Debug("RpcOut failure, restarting channel.\n");
      RpcOut_stop(bdoor->out);
      if (RpcOut_start(bdoor->out)) {
         ret = RpcOut_send(bdoor->out, data, dataLen, &reply, &replyLen);
      } else {
         Warning("Couldn't restart RpcOut channel; bad things may happen "
                 "until the RPC channel is reset.\n");
         chan->outStarted = FALSE;
      }
   }

   /*
    * A lot of this logic is just replicated from rpcout.c:RpcOut_SendOneRaw().
    * Look there for comments about a few details.
    */
   if (result != NULL) {
      if (reply != NULL) {
         *result = Util_SafeMalloc(replyLen + 1);
         memcpy(*result, reply, replyLen);
         (*result)[replyLen] = '\0';
      } else {
         *result = NULL;
      }
   }

   if (resultLen != NULL) {
      *resultLen = replyLen;
   }

exit:
   return ret;
}
Beispiel #4
0
Bool
RpcOut_SendOneRaw(void *request,       // IN: RPCI command
                  size_t reqLen,       // IN: Size of request buffer
                  char **reply,        // OUT: Result
                  size_t *repLen)      // OUT: Length of the result
{
    Bool status;
    RpcOut *out = NULL;
    char const *myReply;
    size_t myRepLen;

    status = FALSE;

    Debug("Rpci: Sending request='%s'\n", (char *)request);
    out = RpcOut_Construct();
    if (out == NULL) {
        myReply = "RpcOut: Unable to create the RpcOut object";
        myRepLen = strlen(myReply);

        goto sent;
    } else if (RpcOut_start(out) == FALSE) {
        myReply = "RpcOut: Unable to open the communication channel";
        myRepLen = strlen(myReply);

        goto sent;
    } else if (RpcOut_send(out, request, reqLen, &myReply, &myRepLen)
               == FALSE) {
        /* We already have the description of the error */
        goto sent;
    }

    status = TRUE;

sent:
    Debug("Rpci: Sent request='%s', reply='%s', len=%"FMTSZ"u, status=%d\n",
          (char *)request, myReply, myRepLen, status);

    if (reply != NULL) {
        /*
         * If we got a non-NULL reply, make a copy of it, because the reply
         * we got back is inside the channel buffer, which will get destroyed
         * at the end of this function.
         */
        if (myReply != NULL) {
            /*
             * We previously used strdup to duplicate myReply, but that
             * breaks if you are sending binary (not string) data over the
             * backdoor. Don't assume the data is a string.
             *
             * myRepLen is strlen(myReply), so we need an extra byte to
             * cover the NUL terminator.
             */
            *reply = malloc(myRepLen + 1);
            if (*reply != NULL) {
                memcpy(*reply, myReply, myRepLen);
                /*
                 * The message layer already writes a trailing NUL but we might
                 * change that someday, so do it again here.
                 */
                (*reply)[myRepLen] = 0;
            }
        } else {
            /*
             * Our reply was NULL, so just pass the NULL back up to the caller.
             */
            *reply = NULL;
        }

        /*
         * Only set the length if the caller wanted it and if we got a good
         * reply.
         */
        if (repLen != NULL && *reply != NULL) {
            *repLen = myRepLen;
        }
    }

    if (out) {
        if (RpcOut_stop(out) == FALSE) {
            /*
             * We couldn't stop the channel. Free anything we allocated, give our
             * client a reply of NULL, and return FALSE.
             */

            if (reply != NULL) {
                free(*reply);
                *reply = NULL;
            }
            Debug("Rpci: unable to close the communication channel\n");
            status = FALSE;
        }

        RpcOut_Destruct(out);
        out = NULL;
    }

    return status;
}