Beispiel #1
0
static enum clnt_stat
clnt_dg_call(CLIENT *clnt,	/* client handle */
	     AUTH *auth,	/* auth handle */
	     rpcproc_t proc,	/* procedure number */
	     xdrproc_t xargs,	/* xdr routine for args */
	     void *argsp,	/* pointer to args */
	     xdrproc_t xresults,	/* xdr routine for results */
	     void *resultsp,	/* pointer to results */
	     struct timeval utimeout
	     /* seconds to wait before giving up */)
{
	struct cu_data *cu = CU_DATA((struct cx_data *)clnt->cl_p1);
	XDR *xdrs;
	size_t outlen = 0;
	struct rpc_msg reply_msg;
	XDR reply_xdrs;
	bool ok;
	int nrefreshes = 2;	/* number of times to refresh cred */
	struct timeval timeout;
	struct pollfd fd;
	int total_time, nextsend_time, tv = 0;
	struct sockaddr *sa;
	socklen_t __attribute__ ((unused)) inlen, salen;
	ssize_t recvlen = 0;
	u_int32_t xid, inval, outval;
	bool slocked = false;
	bool rlocked = false;
	bool once = true;

	outlen = 0;
	rpc_dplx_slc(clnt);
	slocked = true;
	if (cu->cu_total.tv_usec == -1)
		timeout = utimeout;	/* use supplied timeout */
	else
		timeout = cu->cu_total;	/* use default timeout */
	total_time = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
	nextsend_time = cu->cu_wait.tv_sec * 1000 + cu->cu_wait.tv_usec / 1000;

	if (cu->cu_connect && !cu->cu_connected) {
		if (connect
		    (cu->cu_fd, (struct sockaddr *)&cu->cu_raddr,
		     cu->cu_rlen) < 0) {
			cu->cu_error.re_errno = errno;
			cu->cu_error.re_status = RPC_CANTSEND;
			goto out;
		}
		cu->cu_connected = 1;
	}
	if (cu->cu_connected) {
		sa = NULL;
		salen = 0;
	} else {
		sa = (struct sockaddr *)&cu->cu_raddr;
		salen = cu->cu_rlen;
	}

	/* Clean up in case the last call ended in a longjmp(3) call. */
 call_again:
	if (!slocked) {
		rpc_dplx_slc(clnt);
		slocked = true;
	}
	xdrs = &(cu->cu_outxdrs);
	if (cu->cu_async == true && xargs == NULL)
		goto get_reply;
	xdrs->x_op = XDR_ENCODE;
	XDR_SETPOS(xdrs, cu->cu_xdrpos);
	/*
	 * the transaction is the first thing in the out buffer
	 * XXX Yes, and it's in network byte order, so we should to
	 * be careful when we increment it, shouldn't we.
	 */
	xid = ntohl(*(u_int32_t *) (void *)(cu->cu_outbuf));
	xid++;
	*(u_int32_t *) (void *)(cu->cu_outbuf) = htonl(xid);

	if ((!XDR_PUTINT32(xdrs, (int32_t *) &proc))
	    || (!AUTH_MARSHALL(auth, xdrs))
	    || (!AUTH_WRAP(auth, xdrs, xargs, argsp))) {
		cu->cu_error.re_status = RPC_CANTENCODEARGS;
		goto out;
	}
	outlen = (size_t) XDR_GETPOS(xdrs);

 send_again:
	nextsend_time = cu->cu_wait.tv_sec * 1000 + cu->cu_wait.tv_usec / 1000;
	if (sendto(cu->cu_fd, cu->cu_outbuf, outlen, 0, sa, salen) != outlen) {
		cu->cu_error.re_errno = errno;
		cu->cu_error.re_status = RPC_CANTSEND;
		goto out;
	}

 get_reply:
	/*
	 * sub-optimal code appears here because we have
	 * some clock time to spare while the packets are in flight.
	 * (We assume that this is actually only executed once.)
	 */
	rpc_dplx_suc(clnt);
	slocked = false;

	rpc_dplx_rlc(clnt);
	rlocked = true;

	reply_msg.acpted_rply.ar_verf = _null_auth;
	reply_msg.acpted_rply.ar_results.where = NULL;
	reply_msg.acpted_rply.ar_results.proc = (xdrproc_t) xdr_void;

	fd.fd = cu->cu_fd;
	fd.events = POLLIN;
	fd.revents = 0;
	while ((total_time > 0) || once) {
		tv = total_time < nextsend_time ? total_time : nextsend_time;
		once = false;
		switch (poll(&fd, 1, tv)) {
		case 0:
			total_time -= tv;
			rpc_dplx_ruc(clnt);
			rlocked = false;
			if (total_time <= 0) {
				cu->cu_error.re_status = RPC_TIMEDOUT;
				goto out;
			}
			goto send_again;
		case -1:
			if (errno == EINTR)
				continue;
			cu->cu_error.re_status = RPC_CANTRECV;
			cu->cu_error.re_errno = errno;
			goto out;
		}
		break;
	}
#ifdef IP_RECVERR
	if (fd.revents & POLLERR) {
		struct msghdr msg;
		struct cmsghdr *cmsg;
		struct sock_extended_err *e;
		struct sockaddr_in err_addr;
		struct sockaddr_in *sin = (struct sockaddr_in *)&cu->cu_raddr;
		struct iovec iov;
		char *cbuf = (char *)alloca(outlen + 256);
		int ret;

		iov.iov_base = cbuf + 256;
		iov.iov_len = outlen;
		msg.msg_name = (void *)&err_addr;
		msg.msg_namelen = sizeof(err_addr);
		msg.msg_iov = &iov;
		msg.msg_iovlen = 1;
		msg.msg_flags = 0;
		msg.msg_control = cbuf;
		msg.msg_controllen = 256;
		ret = recvmsg(cu->cu_fd, &msg, MSG_ERRQUEUE);
		if (ret >= 0 && memcmp(cbuf + 256, cu->cu_outbuf, ret) == 0
		    && (msg.msg_flags & MSG_ERRQUEUE)
		    && ((msg.msg_namelen == 0 && ret >= 12)
			|| (msg.msg_namelen == sizeof(err_addr)
			    && err_addr.sin_family == AF_INET
			    && memcmp(&err_addr.sin_addr, &sin->sin_addr,
				      sizeof(err_addr.sin_addr)) == 0
			    && err_addr.sin_port == sin->sin_port)))
			for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
			     cmsg = CMSG_NXTHDR(&msg, cmsg))
				if ((cmsg->cmsg_level == SOL_IP)
				    && (cmsg->cmsg_type == IP_RECVERR)) {
					e = (struct sock_extended_err *)
					    CMSG_DATA(cmsg);
					cu->cu_error.re_errno = e->ee_errno;
					cu->cu_error.re_status = RPC_CANTRECV;
				}
	}
#endif

	/* We have some data now */
	do {
		recvlen =
		    recvfrom(cu->cu_fd, cu->cu_inbuf, cu->cu_recvsz, 0, NULL,
			     NULL);
	} while (recvlen < 0 && errno == EINTR);
	if (recvlen < 0 && errno != EWOULDBLOCK) {
		cu->cu_error.re_errno = errno;
		cu->cu_error.re_status = RPC_CANTRECV;
		goto out;
	}

	if (recvlen < sizeof(u_int32_t)) {
		total_time -= tv;
		rpc_dplx_ruc(clnt);
		rlocked = false;
		goto send_again;
	}

	if (cu->cu_async == true)
		inlen = (socklen_t) recvlen;
	else {
		memcpy(&inval, cu->cu_inbuf, sizeof(u_int32_t));
		memcpy(&outval, cu->cu_outbuf, sizeof(u_int32_t));
		if (inval != outval) {
			total_time -= tv;
			rpc_dplx_ruc(clnt);
			rlocked = false;
			goto send_again;
		}
		inlen = (socklen_t) recvlen;
	}

	/*
	 * now decode and validate the response
	 */

	xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int) recvlen, XDR_DECODE);
	ok = xdr_replymsg(&reply_xdrs, &reply_msg);
	/* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */
	if (ok) {
		if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED)
		    && (reply_msg.acpted_rply.ar_stat == SUCCESS))
			cu->cu_error.re_status = RPC_SUCCESS;
		else
			_seterr_reply(&reply_msg, &(cu->cu_error));

		if (cu->cu_error.re_status == RPC_SUCCESS) {
			if (!AUTH_VALIDATE
			    (auth, &reply_msg.acpted_rply.ar_verf)) {
				cu->cu_error.re_status = RPC_AUTHERROR;
				cu->cu_error.re_why = AUTH_INVALIDRESP;
			} else
			    if (!AUTH_UNWRAP
				(auth, &reply_xdrs, xresults, resultsp)) {
				if (cu->cu_error.re_status == RPC_SUCCESS)
					cu->cu_error.re_status =
					    RPC_CANTDECODERES;
			}
			if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
				xdrs->x_op = XDR_FREE;
				(void)xdr_opaque_auth(xdrs,
						      &(reply_msg.acpted_rply.
							ar_verf));
			}
		}
		/* end successful completion */
		/*
		 * If unsuccesful AND error is an authentication error
		 * then refresh credentials and try again, else break
		 */
		else if (cu->cu_error.re_status == RPC_AUTHERROR)
			/* maybe our credentials need to be refreshed ... */
			if (nrefreshes > 0 && AUTH_REFRESH(auth, &reply_msg)) {
				nrefreshes--;
				rpc_dplx_ruc(clnt);
				rlocked = false;
				goto call_again;
			}
		/* end of unsuccessful completion */
	} /* end of valid reply message */
	else
		cu->cu_error.re_status = RPC_CANTDECODERES;

out:
	if (slocked)
		rpc_dplx_suc(clnt);
	if (rlocked)
		rpc_dplx_ruc(clnt);

	return (cu->cu_error.re_status);
}
Beispiel #2
0
static bool
clnt_dg_control(CLIENT *clnt, u_int request, void *info)
{
	struct cu_data *cu = CU_DATA((struct cx_data *)clnt->cl_p1);
	struct netbuf *addr;
	bool rslt = true;

	/* always take recv lock first, if taking both locks together */
	rpc_dplx_rlc(clnt);
	rpc_dplx_slc(clnt);

	switch (request) {
	case CLSET_FD_CLOSE:
		cu->cu_closeit = true;
		rslt = true;
		goto unlock;
	case CLSET_FD_NCLOSE:
		cu->cu_closeit = false;
		rslt = true;
		goto unlock;
	}

	/* for other requests which use info */
	if (info == NULL) {
		rslt = false;
		goto unlock;
	}
	switch (request) {
	case CLSET_TIMEOUT:
		if (time_not_ok((struct timeval *)info)) {
			rslt = false;
			goto unlock;
		}
		cu->cu_total = *(struct timeval *)info;
		break;
	case CLGET_TIMEOUT:
		*(struct timeval *)info = cu->cu_total;
		break;
	case CLGET_SERVER_ADDR:	/* Give him the fd address */
		/* Now obsolete. Only for backward compatibility */
		(void)memcpy(info, &cu->cu_raddr, (size_t) cu->cu_rlen);
		break;
	case CLSET_RETRY_TIMEOUT:
		if (time_not_ok((struct timeval *)info)) {
			rslt = false;
			goto unlock;
		}
		cu->cu_wait = *(struct timeval *)info;
		break;
	case CLGET_RETRY_TIMEOUT:
		*(struct timeval *)info = cu->cu_wait;
		break;
	case CLGET_FD:
		*(int *)info = cu->cu_fd;
		break;
	case CLGET_SVC_ADDR:
		addr = (struct netbuf *)info;
		addr->buf = &cu->cu_raddr;
		addr->len = cu->cu_rlen;
		addr->maxlen = sizeof(cu->cu_raddr);
		break;
	case CLSET_SVC_ADDR:	/* set to new address */
		addr = (struct netbuf *)info;
		if (addr->len < sizeof(cu->cu_raddr)) {
			rslt = false;
			goto unlock;

		}
		(void)memcpy(&cu->cu_raddr, addr->buf, addr->len);
		cu->cu_rlen = addr->len;
		break;
	case CLGET_XID:
		/*
		 * use the knowledge that xid is the
		 * first element in the call structure *.
		 * This will get the xid of the PREVIOUS call
		 */
		*(u_int32_t *) info =
		    ntohl(*(u_int32_t *) (void *)cu->cu_outbuf);
		break;

	case CLSET_XID:
		/* This will set the xid of the NEXT call */
		*(u_int32_t *) (void *)cu->cu_outbuf =
		    htonl(*(u_int32_t *) info - 1);
		/* decrement by 1 as clnt_dg_call() increments once */
		break;

	case CLGET_VERS:
		/*
		 * This RELIES on the information that, in the call body,
		 * the version number field is the fifth field from the
		 * begining of the RPC header. MUST be changed if the
		 * call_struct is changed
		 */
		*(u_int32_t *) info = ntohl(*(u_int32_t *) (void *)
					    (cu->cu_outbuf +
					     4 * BYTES_PER_XDR_UNIT));
		break;

	case CLSET_VERS:
		*(u_int32_t *) (void *)(cu->cu_outbuf + 4 * BYTES_PER_XDR_UNIT)
		    = htonl(*(u_int32_t *) info);
		break;

	case CLGET_PROG:
		/*
		 * This RELIES on the information that, in the call body,
		 * the program number field is the fourth field from the
		 * begining of the RPC header. MUST be changed if the
		 * call_struct is changed
		 */
		*(u_int32_t *) info = ntohl(*(u_int32_t *) (void *)
					    (cu->cu_outbuf +
					     3 * BYTES_PER_XDR_UNIT));
		break;

	case CLSET_PROG:
		*(u_int32_t *) (void *)(cu->cu_outbuf + 3 * BYTES_PER_XDR_UNIT)
		    = htonl(*(u_int32_t *) info);
		break;
	case CLSET_ASYNC:
		cu->cu_async = *(int *)info;
		break;
	case CLSET_CONNECT:
		cu->cu_connect = *(int *)info;
		break;
	default:
		break;
	}

 unlock:
	rpc_dplx_ruc(clnt);
	rpc_dplx_suc(clnt);

	return (rslt);
}
Beispiel #3
0
static bool
clnt_rdma_control(CLIENT *cl, u_int request, void *info)
{
	struct cm_data *cm = CM_DATA((struct cx_data *) cl->cl_p1);
	sigset_t mask;
	bool result = TRUE;

	thr_sigsetmask(SIG_SETMASK, (sigset_t *) 0, &mask); /* XXX */
	/* always take recv lock first if taking together */
	rpc_dplx_rlc(cl); //receive lock clnt
	rpc_dplx_slc(cl); //send lock clnt

	switch (request) {
	case CLSET_FD_CLOSE:
		cm->cm_closeit = TRUE;
		result = TRUE;
		goto unlock;
	case CLSET_FD_NCLOSE:
		cm->cm_closeit = FALSE;
		result = TRUE;
		goto unlock;
	}

	/* for other requests which use info */
	if (info == NULL) {
	    result = FALSE;
	    goto unlock;
	}
	switch (request) {
	case CLSET_TIMEOUT:
		if (time_not_ok((struct timeval *)info)) {
			result = FALSE;
			goto unlock;
		}
		cm->cm_total = *(struct timeval *)info;
		break;
	case CLGET_TIMEOUT:
		*(struct timeval *)info = cm->cm_total;
		break;
	case CLSET_RETRY_TIMEOUT:
		if (time_not_ok((struct timeval *)info)) {
			result = FALSE;
			goto unlock;
		}
		cm->cm_wait = *(struct timeval *)info;
		break;
	case CLGET_RETRY_TIMEOUT:
		*(struct timeval *)info = cm->cm_wait;
		break;
	case CLGET_FD:
		*(RDMAXPRT **)info = cm->cm_xdrs.x_lib[1];
		break;
	case CLGET_XID:
		/*
		 * use the knowledge that xid is the
		 * first element in the call structure *.
		 * This will get the xid of the PREVIOUS call
		 */
		*(u_int32_t *)info = cm->call_msg.rm_xid - 1;
		break;

	case CLSET_XID:
		/* This will set the xid of the NEXT call */
		cm->call_msg.rm_xid = *(u_int32_t *)info;
		break;

	case CLGET_VERS:
		*(u_int32_t *)info = cm->call_msg.rm_call.cb_vers;
		break;

	case CLSET_VERS:
		cm->call_msg.rm_call.cb_vers = *(u_int32_t *)info;
		break;

	case CLGET_PROG:
		*(u_int32_t *)info = cm->call_msg.rm_call.cb_prog;
		break;

	case CLSET_PROG:
		cm->call_msg.rm_call.cb_prog = *(u_int32_t *)info;
		break;

	case CLSET_ASYNC:
		//FIXME cm->cm_async = *(int *)info;
		break;
	case CLSET_CONNECT:
		//FIXMEcm->cm_connect = *(int *)info;
		break;
	default:
		break;
	}

unlock:
	rpc_dplx_ruc(cl);
	rpc_dplx_suc(cl);
	return (result);
}