extern int MoreUNIXCopyDescriptorToDescriptor(int source, int dest) // See comment in header. { int err; bool done; char * buffer; static const size_t kBufferSize = 64 * 1024; assert(source >= 0); assert(dest >= 0); err = 0; buffer = (char *) malloc(kBufferSize); if (buffer == NULL) { err = ENOMEM; } if (err == 0) { done = false; do { size_t bytesRead; err = MoreUNIXRead(source, buffer, kBufferSize, &bytesRead); if (err == EPIPE) { done = true; err = 0; } if (err == 0) { err = MoreUNIXWrite(dest, buffer, bytesRead, NULL); } } while (err == 0 && !done); } free(buffer); return err; }
static int ConnectionRPC( ConnectionRef conn, const PacketHeader * request, PacketHeader * reply, size_t replySize ) // Perform an RPC (Remote Procedure Call) with the server. That is, send // the server a packet and wait for a reply. You can only use this on // connections that are not in listening mode. // // conn must be a valid connection // // packet must be a valid, ready-to-send, packet // // reply and replySize specify a buffer where the reply packet is placed; // reply size must not be NULL; replySize must not be less that the // packet header size (sizeof(PacketHeader)); if the reply packet is bigger // than replySize, the data that won't fit is discarded; you can detect this // by looking at reply->fSize // // Returns an errno-style error code // On success, the buffer specified by reply and replySize will contain the // reply packet; on error, the contents of that buffer is invalid; also, // if this routine errors the connection is no longer useful (conn is still // valid, but you can't use it to transmit any more data) { int err; assert(conn != NULL); assert(conn->fSockFD != -1); // connection must not be shut down assert(conn->fSockCF == NULL); // RPC and listening are mutually exclusive // because unsolicited packet might get mixed up // with the reply assert(request != NULL); assert(request->fMagic == kPacketMagic); assert(request->fSize >= sizeof(PacketHeader)); assert(reply != NULL); assert(replySize >= sizeof(PacketHeader)); // Send the request. err = ConnectionSend(conn, request); // Read and validate the reply header. if (err == 0) { err = MoreUNIXRead(conn->fSockFD, reply, sizeof(PacketHeader), NULL); } if ( (err == 0) && (reply->fMagic != kPacketMagic) ) { fprintf(stderr, "ConnectionRPC: Bad magic (%.4s).\n", (char *) &reply->fMagic); err = EINVAL; } if ( (err == 0) && (reply->fType != kPacketTypeReply) ) { fprintf(stderr, "ConnectionRPC: Type wrong (%.4s).\n", (char *) &reply->fType); err = EINVAL; } if ( (err == 0) && (reply->fID != request->fID) ) { fprintf(stderr, "ConnectionRPC: ID mismatch (%" PRId32 ").\n", reply->fID); err = EINVAL; } if ( (err == 0) && ( (reply->fSize < sizeof(PacketHeader)) || (reply->fSize > kPacketMaximumSize) ) ) { fprintf(stderr, "ConnectionRPC: Bogus packet size (%" PRIu32 ").\n", reply->fSize); err = EINVAL; } // Read the packet payload that will fit in the reply buffer. if ( (err == 0) && (reply->fSize > sizeof(PacketHeader)) ) { uint32_t payloadToRead; if (reply->fSize > replySize) { payloadToRead = replySize; } else { payloadToRead = reply->fSize; } payloadToRead -= sizeof(PacketHeader); err = MoreUNIXRead(conn->fSockFD, ((char *) reply) + sizeof(PacketHeader), payloadToRead, NULL); } // Discard any remaining packet payload that will fit in the reply buffer. // The addition check in the next line is necessary to avoid the undefined behaviour // of malloc(0) in the dependent block. if ( (err == 0) && (reply->fSize > replySize) ) { uint32_t payloadToJunk; void * junkBuf; payloadToJunk = reply->fSize - replySize; junkBuf = malloc(payloadToJunk); if (junkBuf == NULL) { err = ENOMEM; } if (err == 0) { err = MoreUNIXRead(conn->fSockFD, junkBuf, payloadToJunk, NULL); } free(junkBuf); } // Any errors cause us to immediately shut down our connection because we // we're no longer sure of the state of the channel (that is, did we leave // half a packet stuck in the pipe). if (err != 0) { ConnectionShutdown(conn); } return err; }