/* * Read data until we get a complete message to process */ static void virNetServerClientDispatchRead(virNetServerClientPtr client) { readmore: if (client->rx->nfds == 0) { if (virNetServerClientRead(client) < 0) { client->wantClose = true; return; /* Error */ } } if (client->rx->bufferOffset < client->rx->bufferLength) return; /* Still not read enough */ /* Either done with length word header */ if (client->rx->bufferLength == VIR_NET_MESSAGE_LEN_MAX) { if (virNetMessageDecodeLength(client->rx) < 0) { client->wantClose = true; return; } virNetServerClientUpdateEvent(client); /* Try and read payload immediately instead of going back into poll() because chances are the data is already waiting for us */ goto readmore; } else { /* Grab the completed message */ virNetMessagePtr msg = client->rx; virNetMessagePtr response = NULL; virNetServerClientFilterPtr filter; size_t i; /* Decode the header so we can use it for routing decisions */ if (virNetMessageDecodeHeader(msg) < 0) { virNetMessageFree(msg); client->wantClose = true; return; } /* Now figure out if we need to read more data to get some * file descriptors */ if (msg->header.type == VIR_NET_CALL_WITH_FDS && virNetMessageDecodeNumFDs(msg) < 0) { virNetMessageFree(msg); client->wantClose = true; return; /* Error */ } /* Try getting the file descriptors (may fail if blocking) */ for (i = msg->donefds ; i < msg->nfds ; i++) { int rv; if ((rv = virNetSocketRecvFD(client->sock, &(msg->fds[i]))) < 0) { virNetMessageFree(msg); client->wantClose = true; return; } if (rv == 0) /* Blocking */ break; msg->donefds++; } /* Need to poll() until FDs arrive */ if (msg->donefds < msg->nfds) { /* Because DecodeHeader/NumFDs reset bufferOffset, we * put it back to what it was, so everything works * again next time we run this method */ client->rx->bufferOffset = client->rx->bufferLength; return; } /* Definitely finished reading, so remove from queue */ virNetMessageQueueServe(&client->rx); PROBE(RPC_SERVER_CLIENT_MSG_RX, "client=%p len=%zu prog=%u vers=%u proc=%u type=%u status=%u serial=%u", client, msg->bufferLength, msg->header.prog, msg->header.vers, msg->header.proc, msg->header.type, msg->header.status, msg->header.serial); if (virKeepAliveCheckMessage(client->keepalive, msg, &response)) { virNetMessageFree(msg); client->nrequests--; msg = NULL; if (response && virNetServerClientSendMessageLocked(client, response) < 0) virNetMessageFree(response); } /* Maybe send off for queue against a filter */ if (msg) { filter = client->filters; while (filter) { int ret = filter->func(client, msg, filter->opaque); if (ret < 0) { virNetMessageFree(msg); msg = NULL; if (ret < 0) client->wantClose = true; break; } if (ret > 0) { msg = NULL; break; } filter = filter->next; } } /* Send off to for normal dispatch to workers */ if (msg) { virObjectRef(client); if (!client->dispatchFunc || client->dispatchFunc(client, msg, client->dispatchOpaque) < 0) { virNetMessageFree(msg); client->wantClose = true; virObjectUnref(client); return; } } /* Possibly need to create another receive buffer */ if (client->nrequests < client->nrequests_max) { if (!(client->rx = virNetMessageNew(true))) { client->wantClose = true; } else { client->rx->bufferLength = VIR_NET_MESSAGE_LEN_MAX; if (VIR_ALLOC_N(client->rx->buffer, client->rx->bufferLength) < 0) { virReportOOMError(); client->wantClose = true; } else { client->nrequests++; } } } virNetServerClientUpdateEvent(client); } }
static ssize_t virNetClientIOHandleInput(virNetClientPtr client) { /* Read as much data as is available, until we get * EAGAIN */ for (;;) { ssize_t ret; if (client->msg.nfds == 0) { ret = virNetClientIOReadMessage(client); if (ret < 0) return -1; if (ret == 0) return 0; /* Blocking on read */ } /* Check for completion of our goal */ if (client->msg.bufferOffset == client->msg.bufferLength) { if (client->msg.bufferOffset == 4) { ret = virNetMessageDecodeLength(&client->msg); if (ret < 0) return -1; /* * We'll carry on around the loop to immediately * process the message body, because it has probably * already arrived. Worst case, we'll get EAGAIN on * next iteration. */ } else { if (virNetMessageDecodeHeader(&client->msg) < 0) return -1; if (client->msg.header.type == VIR_NET_REPLY_WITH_FDS) { size_t i; if (virNetMessageDecodeNumFDs(&client->msg) < 0) return -1; for (i = client->msg.donefds ; i < client->msg.nfds ; i++) { int rv; if ((rv = virNetSocketRecvFD(client->sock, &(client->msg.fds[i]))) < 0) return -1; if (rv == 0) /* Blocking */ break; client->msg.donefds++; } if (client->msg.donefds < client->msg.nfds) { /* Because DecodeHeader/NumFDs reset bufferOffset, we * put it back to what it was, so everything works * again next time we run this method */ client->msg.bufferOffset = client->msg.bufferLength; return 0; /* Blocking on more fds */ } } ret = virNetClientCallDispatch(client); client->msg.bufferOffset = client->msg.bufferLength = 0; /* * We've completed one call, but we don't want to * spin around the loop forever if there are many * incoming async events, or replies for other * thread's RPC calls. We want to get out & let * any other thread take over as soon as we've * got our reply. When SASL is active though, we * may have read more data off the wire than we * initially wanted & cached it in memory. In this * case, poll() would not detect that there is more * ready todo. * * So if SASL is active *and* some SASL data is * already cached, then we'll process that now, * before returning. */ if (ret == 0 && virNetSocketHasCachedData(client->sock)) continue; return ret; } } } }