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); }
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); }
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); }