/* * @server: the unlocked server object * @client: the unlocked client object * @msg: the complete incoming method call, with header already decoded * * This method is used to dispatch a message representing an * incoming method call from a client. It decodes the payload * to obtain method call arguments, invokves the method and * then sends a reply packet with the return values * * Returns 0 if the reply was sent, or -1 upon fatal error */ static int virNetServerProgramDispatchCall(virNetServerProgramPtr prog, virNetServerPtr server, virNetServerClientPtr client, virNetMessagePtr msg) { char *arg = NULL; char *ret = NULL; int rv = -1; virNetServerProgramProcPtr dispatcher; virNetMessageError rerr; size_t i; virIdentityPtr identity = NULL; memset(&rerr, 0, sizeof(rerr)); if (msg->header.status != VIR_NET_OK) { virReportError(VIR_ERR_RPC, _("Unexpected message status %u"), msg->header.status); goto error; } dispatcher = virNetServerProgramGetProc(prog, msg->header.proc); if (!dispatcher) { virReportError(VIR_ERR_RPC, _("unknown procedure: %d"), msg->header.proc); goto error; } /* If client is marked as needing auth, don't allow any RPC ops * which are except for authentication ones */ if (virNetServerClientNeedAuth(client) && dispatcher->needAuth) { /* Explicitly *NOT* calling remoteDispatchAuthError() because we want back-compatibility with libvirt clients which don't support the VIR_ERR_AUTH_FAILED error code */ virReportError(VIR_ERR_RPC, "%s", _("authentication required")); goto error; } if (VIR_ALLOC_N(arg, dispatcher->arg_len) < 0) goto error; if (VIR_ALLOC_N(ret, dispatcher->ret_len) < 0) goto error; if (virNetMessageDecodePayload(msg, dispatcher->arg_filter, arg) < 0) goto error; if (!(identity = virNetServerClientGetIdentity(client))) goto error; if (virIdentitySetCurrent(identity) < 0) goto error; /* * When the RPC handler is called: * * - Server object is unlocked * - Client object is unlocked * * Without locking, it is safe to use: * * 'args and 'ret' */ rv = (dispatcher->func)(server, client, msg, &rerr, arg, ret); if (virIdentitySetCurrent(NULL) < 0) goto error; /* * If rv == 1, this indicates the dispatch func has * populated 'msg' with a list of FDs to return to * the caller. * * Otherwise we must clear out the FDs we got from * the client originally. * */ if (rv != 1) { for (i = 0; i < msg->nfds; i++) VIR_FORCE_CLOSE(msg->fds[i]); VIR_FREE(msg->fds); msg->nfds = 0; } xdr_free(dispatcher->arg_filter, arg); if (rv < 0) goto error; /* Return header. We're re-using same message object, so * only need to tweak type/status fields */ /*msg->header.prog = msg->header.prog;*/ /*msg->header.vers = msg->header.vers;*/ /*msg->header.proc = msg->header.proc;*/ msg->header.type = msg->nfds ? VIR_NET_REPLY_WITH_FDS : VIR_NET_REPLY; /*msg->header.serial = msg->header.serial;*/ msg->header.status = VIR_NET_OK; if (virNetMessageEncodeHeader(msg) < 0) { xdr_free(dispatcher->ret_filter, ret); goto error; } if (msg->nfds && virNetMessageEncodeNumFDs(msg) < 0) { xdr_free(dispatcher->ret_filter, ret); goto error; } if (virNetMessageEncodePayload(msg, dispatcher->ret_filter, ret) < 0) { xdr_free(dispatcher->ret_filter, ret); goto error; } xdr_free(dispatcher->ret_filter, ret); VIR_FREE(arg); VIR_FREE(ret); virObjectUnref(identity); /* Put reply on end of tx queue to send out */ return virNetServerClientSendMessage(client, msg); error: /* Bad stuff (de-)serializing message, but we have an * RPC error message we can send back to the client */ rv = virNetServerProgramSendReplyError(prog, client, msg, &rerr, &msg->header); VIR_FREE(arg); VIR_FREE(ret); virObjectUnref(identity); return rv; }
int virNetClientProgramCall(virNetClientProgramPtr prog, virNetClientPtr client, unsigned serial, int proc, size_t noutfds, int *outfds, size_t *ninfds, int **infds, xdrproc_t args_filter, void *args, xdrproc_t ret_filter, void *ret) { virNetMessagePtr msg; size_t i; if (infds) *infds = NULL; if (ninfds) *ninfds = 0; if (!(msg = virNetMessageNew(false))) return -1; msg->header.prog = prog->program; msg->header.vers = prog->version; msg->header.status = VIR_NET_OK; msg->header.type = noutfds ? VIR_NET_CALL_WITH_FDS : VIR_NET_CALL; msg->header.serial = serial; msg->header.proc = proc; msg->nfds = noutfds; if (VIR_ALLOC_N(msg->fds, msg->nfds) < 0) { virReportOOMError(); goto error; } for (i = 0; i < msg->nfds; i++) msg->fds[i] = -1; for (i = 0; i < msg->nfds; i++) { if ((msg->fds[i] = dup(outfds[i])) < 0) { virReportSystemError(errno, _("Cannot duplicate FD %d"), outfds[i]); goto error; } if (virSetInherit(msg->fds[i], false) < 0) { virReportSystemError(errno, _("Cannot set close-on-exec %d"), msg->fds[i]); goto error; } } if (virNetMessageEncodeHeader(msg) < 0) goto error; if (msg->nfds && virNetMessageEncodeNumFDs(msg) < 0) goto error; if (virNetMessageEncodePayload(msg, args_filter, args) < 0) goto error; if (virNetClientSendWithReply(client, msg) < 0) goto error; /* None of these 3 should ever happen here, because * virNetClientSend should have validated the reply, * but it doesn't hurt to check again. */ if (msg->header.type != VIR_NET_REPLY && msg->header.type != VIR_NET_REPLY_WITH_FDS) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unexpected message type %d"), msg->header.type); goto error; } if (msg->header.proc != proc) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unexpected message proc %d != %d"), msg->header.proc, proc); goto error; } if (msg->header.serial != serial) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unexpected message serial %d != %d"), msg->header.serial, serial); goto error; } switch (msg->header.status) { case VIR_NET_OK: if (infds && ninfds) { *ninfds = msg->nfds; if (VIR_ALLOC_N(*infds, *ninfds) < 0) { virReportOOMError(); goto error; } for (i = 0; i < *ninfds; i++) (*infds)[i] = -1; for (i = 0; i < *ninfds; i++) { if (((*infds)[i] = dup(msg->fds[i])) < 0) { virReportSystemError(errno, _("Cannot duplicate FD %d"), msg->fds[i]); goto error; } if (virSetInherit((*infds)[i], false) < 0) { virReportSystemError(errno, _("Cannot set close-on-exec %d"), (*infds)[i]); goto error; } } } if (virNetMessageDecodePayload(msg, ret_filter, ret) < 0) goto error; break; case VIR_NET_ERROR: virNetClientProgramDispatchError(prog, msg); goto error; default: virReportError(VIR_ERR_RPC, _("Unexpected message status %d"), msg->header.status); goto error; } virNetMessageFree(msg); return 0; error: virNetMessageFree(msg); if (infds && ninfds) { for (i = 0; i < *ninfds; i++) VIR_FORCE_CLOSE((*infds)[i]); } return -1; }