/* * Called when the stream is signalled has being able to accept * data writes. Will process all pending incoming messages * until they're all gone, or I/O blocks * * Returns 0 on success, or -1 upon fatal error */ static int daemonStreamHandleWrite(virNetServerClientPtr client, daemonClientStream *stream) { VIR_DEBUG("client=%p, stream=%p", client, stream); while (stream->rx && !stream->closed) { virNetMessagePtr msg = stream->rx; int ret; switch (msg->header.status) { case VIR_NET_OK: ret = daemonStreamHandleFinish(client, stream, msg); break; case VIR_NET_CONTINUE: ret = daemonStreamHandleWriteData(client, stream, msg); break; case VIR_NET_ERROR: default: ret = daemonStreamHandleAbort(client, stream, msg); break; } if (ret > 0) break; /* still processing data from msg */ virNetMessageQueueServe(&stream->rx); if (ret < 0) { virNetMessageFree(msg); virNetServerClientImmediateClose(client); return -1; } /* 'CONTINUE' messages don't send a reply (unless error * occurred), so to release the 'msg' object we need to * send a fake zero-length reply. Nothing actually gets * onto the wire, but this causes the client to reset * its active request count / throttling */ if (msg->header.status == VIR_NET_CONTINUE) { virNetMessageClear(msg); msg->header.type = VIR_NET_REPLY; if (virNetServerClientSendMessage(client, msg) < 0) { virNetMessageFree(msg); virNetServerClientImmediateClose(client); return -1; } } } return 0; }
static void virNetServerHandleJob(void *jobOpaque, void *opaque) { virNetServerPtr srv = opaque; virNetServerJobPtr job = jobOpaque; VIR_DEBUG("server=%p client=%p message=%p prog=%p", srv, job->client, job->msg, job->prog); if (!job->prog) { /* Only send back an error for type == CALL. Other * message types are not expecting replies, so we * must just log it & drop them */ if (job->msg->header.type == VIR_NET_CALL || job->msg->header.type == VIR_NET_CALL_WITH_FDS) { if (virNetServerProgramUnknownError(job->client, job->msg, &job->msg->header) < 0) goto error; } else { VIR_INFO("Dropping client mesage, unknown program %d version %d type %d proc %d", job->msg->header.prog, job->msg->header.vers, job->msg->header.type, job->msg->header.proc); /* Send a dummy reply to free up 'msg' & unblock client rx */ virNetMessageClear(job->msg); job->msg->header.type = VIR_NET_REPLY; if (virNetServerClientSendMessage(job->client, job->msg) < 0) goto error; } goto cleanup; } if (virNetServerProgramDispatch(job->prog, srv, job->client, job->msg) < 0) goto error; virNetServerLock(srv); virNetServerProgramFree(job->prog); virNetServerUnlock(srv); cleanup: virNetServerClientFree(job->client); VIR_FREE(job); return; error: virNetServerProgramFree(job->prog); virNetMessageFree(job->msg); virNetServerClientClose(job->client); virNetServerClientFree(job->client); VIR_FREE(job); }
static int virNetServerProcessMsg(virNetServerPtr srv, virNetServerClientPtr client, virNetServerProgramPtr prog, virNetMessagePtr msg) { int ret = -1; if (!prog) { /* Only send back an error for type == CALL. Other * message types are not expecting replies, so we * must just log it & drop them */ if (msg->header.type == VIR_NET_CALL || msg->header.type == VIR_NET_CALL_WITH_FDS) { if (virNetServerProgramUnknownError(client, msg, &msg->header) < 0) goto cleanup; } else { VIR_INFO("Dropping client mesage, unknown program %d version %d type %d proc %d", msg->header.prog, msg->header.vers, msg->header.type, msg->header.proc); /* Send a dummy reply to free up 'msg' & unblock client rx */ virNetMessageClear(msg); msg->header.type = VIR_NET_REPLY; if (virNetServerClientSendMessage(client, msg) < 0) goto cleanup; } goto done; } if (virNetServerProgramDispatch(prog, srv, client, msg) < 0) goto cleanup; done: ret = 0; cleanup: return ret; }
/* * @stream: an unused client stream * * Frees the memory associated with this inactive client * stream */ int daemonFreeClientStream(virNetServerClientPtr client, daemonClientStream *stream) { virNetMessagePtr msg; int ret = 0; if (!stream) return 0; stream->refs--; if (stream->refs) return 0; VIR_DEBUG("client=%p, proc=%d, serial=%d", client, stream->procedure, stream->serial); virObjectUnref(stream->prog); msg = stream->rx; while (msg) { virNetMessagePtr tmp = msg->next; if (client) { /* Send a dummy reply to free up 'msg' & unblock client rx */ virNetMessageClear(msg); msg->header.type = VIR_NET_REPLY; if (virNetServerClientSendMessage(client, msg) < 0) { virNetServerClientImmediateClose(client); virNetMessageFree(msg); ret = -1; } } else { virNetMessageFree(msg); } msg = tmp; } virStreamFree(stream->st); VIR_FREE(stream); return ret; }
/* * @server: the unlocked server object * @client: the unlocked client object * @msg: the complete incoming message packet, with header already decoded * * This function is intended to be called from worker threads * when an incoming message is ready to be dispatched for * execution. * * Upon successful return the '@msg' instance will be released * by this function (or more often, reused to send a reply). * Upon failure, the '@msg' must be freed by the caller. * * Returns 0 if the message was dispatched, -1 upon fatal error */ int virNetServerProgramDispatch(virNetServerProgramPtr prog, virNetServerPtr server, virNetServerClientPtr client, virNetMessagePtr msg) { int ret = -1; virNetMessageError rerr; memset(&rerr, 0, sizeof(rerr)); VIR_DEBUG("prog=%d ver=%d type=%d status=%d serial=%u proc=%d", msg->header.prog, msg->header.vers, msg->header.type, msg->header.status, msg->header.serial, msg->header.proc); /* Check version, etc. */ if (msg->header.prog != prog->program) { virReportError(VIR_ERR_RPC, _("program mismatch (actual %x, expected %x)"), msg->header.prog, prog->program); goto error; } if (msg->header.vers != prog->version) { virReportError(VIR_ERR_RPC, _("version mismatch (actual %x, expected %x)"), msg->header.vers, prog->version); goto error; } switch (msg->header.type) { case VIR_NET_CALL: case VIR_NET_CALL_WITH_FDS: ret = virNetServerProgramDispatchCall(prog, server, client, msg); break; case VIR_NET_STREAM: /* Since stream data is non-acked, async, we may continue to receive * stream packets after we closed down a stream. Just drop & ignore * these. */ VIR_INFO("Ignoring unexpected stream data serial=%u proc=%d status=%d", msg->header.serial, msg->header.proc, msg->header.status); /* Send a dummy reply to free up 'msg' & unblock client rx */ virNetMessageClear(msg); msg->header.type = VIR_NET_REPLY; if (virNetServerClientSendMessage(client, msg) < 0) { ret = -1; goto cleanup; } ret = 0; break; default: virReportError(VIR_ERR_RPC, _("Unexpected message type %u"), msg->header.type); goto error; } return ret; error: if (msg->header.type == VIR_NET_CALL || msg->header.type == VIR_NET_CALL_WITH_FDS) { ret = virNetServerProgramSendReplyError(prog, client, msg, &rerr, &msg->header); } else { /* Send a dummy reply to free up 'msg' & unblock client rx */ virNetMessageClear(msg); msg->header.type = VIR_NET_REPLY; if (virNetServerClientSendMessage(client, msg) < 0) { ret = -1; goto cleanup; } ret = 0; } cleanup: return ret; }
/* * Process all queued client->tx messages until * we would block on I/O */ static void virNetServerClientDispatchWrite(virNetServerClientPtr client) { while (client->tx) { if (client->tx->bufferOffset < client->tx->bufferLength) { ssize_t ret; ret = virNetServerClientWrite(client); if (ret < 0) { client->wantClose = true; return; } if (ret == 0) return; /* Would block on write EAGAIN */ } if (client->tx->bufferOffset == client->tx->bufferLength) { virNetMessagePtr msg; size_t i; for (i = client->tx->donefds ; i < client->tx->nfds ; i++) { int rv; if ((rv = virNetSocketSendFD(client->sock, client->tx->fds[i])) < 0) { client->wantClose = true; return; } if (rv == 0) /* Blocking */ return; client->tx->donefds++; } #if HAVE_SASL /* Completed this 'tx' operation, so now read for all * future rx/tx to be under a SASL SSF layer */ if (client->sasl) { virNetSocketSetSASLSession(client->sock, client->sasl); virObjectUnref(client->sasl); client->sasl = NULL; } #endif /* Get finished msg from head of tx queue */ msg = virNetMessageQueueServe(&client->tx); if (msg->tracked) { client->nrequests--; /* See if the recv queue is currently throttled */ if (!client->rx && client->nrequests < client->nrequests_max) { /* Ready to recv more messages */ virNetMessageClear(msg); msg->bufferLength = VIR_NET_MESSAGE_LEN_MAX; if (VIR_ALLOC_N(msg->buffer, msg->bufferLength) < 0) { virReportOOMError(); virNetMessageFree(msg); return; } client->rx = msg; msg = NULL; client->nrequests++; } } virNetMessageFree(msg); virNetServerClientUpdateEvent(client); if (client->delayedClose) client->wantClose = true; } } }