/* * Get a copy of the current port maps. * Calls the pmap service remotely to do get the maps. */ struct pmaplist * pmap_getmaps(struct sockaddr_in *address) { struct pmaplist *head = NULL; int sock = -1; struct timeval minutetimeout; CLIENT *client; assert(address != NULL); minutetimeout.tv_sec = 60; minutetimeout.tv_usec = 0; address->sin_port = htons(PMAPPORT); client = clnttcp_create(address, PMAPPROG, PMAPVERS, &sock, 50, 500); if (client != NULL) { if (CLNT_CALL(client, (rpcproc_t)PMAPPROC_DUMP, (xdrproc_t)xdr_void, NULL, (xdrproc_t)xdr_pmaplist, &head, minutetimeout) != RPC_SUCCESS) { clnt_perror(client, "pmap_getmaps rpc problem"); } CLNT_DESTROY(client); } address->sin_port = 0; return (head); }
/* * Set a mapping between program,version and port. * Calls the pmap service remotely to do the mapping. */ bool_t pmap_set(u_long program, u_long version, u_int protocol, int iport) { struct sockaddr_in myaddress; int sock = -1; CLIENT *client; struct pmap parms; bool_t rslt; u_short port = iport; if (get_myaddress(&myaddress) != 0) return (FALSE); myaddress.sin_addr.s_addr = htonl(INADDR_LOOPBACK); client = clntudp_bufcreate(&myaddress, PMAPPROG, PMAPVERS, timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); if (client == NULL) return (FALSE); parms.pm_prog = program; parms.pm_vers = version; parms.pm_prot = protocol; parms.pm_port = port; if (CLNT_CALL(client, PMAPPROC_SET, xdr_pmap, &parms, xdr_bool, &rslt, tottimeout) != RPC_SUCCESS) { int save_errno = errno; clnt_perror(client, "Cannot register service"); errno = save_errno; return (FALSE); } CLNT_DESTROY(client); if (sock != -1) (void)close(sock); return (rslt); }
/* * Remove the mapping between program,version and port. * Calls the pmap service remotely to do the un-mapping. */ bool_t pmap_unset(u_long program, u_long version) { struct sockaddr_in myaddress; int sock = -1; CLIENT *client; struct pmap parms; bool_t rslt; if (get_myaddress(&myaddress) != 0) return (FALSE); myaddress.sin_addr.s_addr = htonl(INADDR_LOOPBACK); client = clntudp_bufcreate(&myaddress, PMAPPROG, PMAPVERS, timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); if (client == NULL) return (FALSE); parms.pm_prog = program; parms.pm_vers = version; parms.pm_port = parms.pm_prot = 0; CLNT_CALL(client, PMAPPROC_UNSET, xdr_pmap, &parms, xdr_bool, &rslt, tottimeout); CLNT_DESTROY(client); if (sock != -1) (void)close(sock); return (rslt); }
/* * pmapper remote-call-service interface. * This routine is used to call the pmapper remote call service * which will look up a service program in the port maps, and then * remotely call that routine with the given parameters. This allows * programs to do a lookup and call in one step. */ enum clnt_stat pmap_rmtcall(struct sockaddr_in *addr, u_long prog, u_long vers, u_long proc, xdrproc_t xdrargs, caddr_t argsp, xdrproc_t xdrres, caddr_t resp, struct timeval tout, u_long *port_ptr) { int sock = -1; CLIENT *client; struct rmtcallargs a; struct rmtcallres r; enum clnt_stat stat; addr->sin_port = htons(PMAPPORT); client = clntudp_create(addr, PMAPPROG, PMAPVERS, timeout, &sock); if (client != NULL) { a.prog = prog; a.vers = vers; a.proc = proc; a.args_ptr = argsp; a.xdr_args = xdrargs; r.port_ptr = port_ptr; r.results_ptr = resp; r.xdr_results = xdrres; stat = CLNT_CALL(client, PMAPPROC_CALLIT, xdr_rmtcall_args, &a, xdr_rmtcallres, &r, tout); CLNT_DESTROY(client); } else { stat = RPC_FAILED; } if (sock != -1) (void)close(sock); addr->sin_port = 0; return (stat); }
/* * Remove the mapping between program,version and port. * Calls the pmap service remotely to do the un-mapping. */ bool_t pmap_unset (u_long program, u_long version) { struct sockaddr_in myaddress; int socket = -1; CLIENT *client; struct pmap parms; bool_t rslt; if (!__get_myaddress (&myaddress)) return FALSE; client = INTUSE(clntudp_bufcreate) (&myaddress, PMAPPROG, PMAPVERS, timeout, &socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); if (client == (CLIENT *) NULL) return FALSE; parms.pm_prog = program; parms.pm_vers = version; parms.pm_port = parms.pm_prot = 0; CLNT_CALL (client, PMAPPROC_UNSET, (xdrproc_t)INTUSE(xdr_pmap), (caddr_t)&parms, (xdrproc_t)INTUSE(xdr_bool), (caddr_t)&rslt, tottimeout); CLNT_DESTROY (client); /* (void)close(socket); CLNT_DESTROY already closed it */ return rslt; }
/* * Find the mapped port for program,version. * Calls the pmap service remotely to do the lookup. * Returns 0 if no map exists. */ u_short pmap_getport(struct sockaddr_in *address, u_long program, u_long version, u_int protocol) { u_short port = 0; int sock = -1; CLIENT *client; struct pmap parms; assert(address != NULL); address->sin_port = htons(PMAPPORT); client = clntudp_bufcreate(address, PMAPPROG, PMAPVERS, timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); if (client != NULL) { parms.pm_prog = program; parms.pm_vers = version; parms.pm_prot = protocol; parms.pm_port = 0; /* not needed or used */ if (CLNT_CALL(client, (rpcproc_t)PMAPPROC_GETPORT, (xdrproc_t)xdr_pmap, &parms, (xdrproc_t)xdr_u_short, &port, tottimeout) != RPC_SUCCESS){ rpc_createerr.cf_stat = RPC_PMAPFAILURE; clnt_geterr(client, &rpc_createerr.cf_error); } else if (port == 0) { rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; } CLNT_DESTROY(client); } address->sin_port = 0; return (port); }
/* * Set a mapping between program,version and port. * Calls the pmap service remotely to do the mapping. */ bool_t pmap_set (u_long program, u_long version, int protocol, u_short port) { struct sockaddr_in myaddress; int socket = -1; CLIENT *client; struct pmap parms; bool_t rslt; if (!__get_myaddress (&myaddress)) return FALSE; client = INTUSE(clntudp_bufcreate) (&myaddress, PMAPPROG, PMAPVERS, timeout, &socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); if (client == (CLIENT *) NULL) return (FALSE); parms.pm_prog = program; parms.pm_vers = version; parms.pm_prot = protocol; parms.pm_port = port; if (CLNT_CALL (client, PMAPPROC_SET, (xdrproc_t)INTUSE(xdr_pmap), (caddr_t)&parms, (xdrproc_t)INTUSE(xdr_bool), (caddr_t)&rslt, tottimeout) != RPC_SUCCESS) { clnt_perror (client, _("Cannot register service")); rslt = FALSE; } CLNT_DESTROY (client); /* (void)close(socket); CLNT_DESTROY closes it */ return rslt; }
/* * Get a copy of the current port maps. * Calls the pmap service remotely to do get the maps. */ struct pmaplist * pmap_getmaps (struct sockaddr_in *address) { struct pmaplist *head = (struct pmaplist *) NULL; struct timeval minutetimeout; CLIENT *client; bool closeit = false; minutetimeout.tv_sec = 60; minutetimeout.tv_usec = 0; address->sin_port = htons (PMAPPORT); /* Don't need a reserved port to get ports from the portmapper. */ int socket = __get_socket (address); if (socket != -1) closeit = true; client = clnttcp_create (address, PMAPPROG, PMAPVERS, &socket, 50, 500); if (client != (CLIENT *) NULL) { if (CLNT_CALL (client, PMAPPROC_DUMP, (xdrproc_t)xdr_void, NULL, (xdrproc_t)xdr_pmaplist, (caddr_t)&head, minutetimeout) != RPC_SUCCESS) { clnt_perror (client, _("pmap_getmaps.c: rpc problem")); } CLNT_DESTROY (client); } /* We only need to close the socket here if we opened it. */ if (closeit) close_not_cancel (socket); address->sin_port = 0; return head; }
/* * Get a copy of the current port maps. * Calls the pmap service remotely to do get the maps. */ struct pmaplist * pmap_getmaps (struct sockaddr_in *address) { struct pmaplist *head = (struct pmaplist *) NULL; int _socket = -1; struct timeval minutetimeout; CLIENT *client; minutetimeout.tv_sec = 60; minutetimeout.tv_usec = 0; address->sin_port = htons (PMAPPORT); /* Don't need a reserved port to get ports from the portmapper. */ client = clnttcp_create (address, PMAPPROG, PMAPVERS, &_socket, 50, 500); if (client != (CLIENT *) NULL) { if (CLNT_CALL (client, PMAPPROC_DUMP, (xdrproc_t)xdr_void, NULL, (xdrproc_t)xdr_pmaplist, (caddr_t)&head, minutetimeout) != RPC_SUCCESS) { clnt_perror (client, _("pmap_getmaps rpc problem")); } CLNT_DESTROY (client); } /* (void)close(_socket); CLNT_DESTROY already closed it */ address->sin_port = 0; return head; }
/* * Set a mapping between program,version and port. * Calls the pmap service remotely to do the mapping. */ bool_t pmap_set( rpcprog_t program, rpcvers_t version, rpcprot_t protocol, u_int port) { struct sockaddr_in myaddress; int socket = -1; register CLIENT *client; struct pmap parms; bool_t rslt; get_myaddress(&myaddress); client = clntudp_bufcreate(&myaddress, PMAPPROG, PMAPVERS, timeout, &socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); if (client == (CLIENT *)NULL) return (FALSE); parms.pm_prog = program; parms.pm_vers = version; parms.pm_prot = protocol; parms.pm_port = port; if (CLNT_CALL(client, PMAPPROC_SET, xdr_pmap, &parms, xdr_bool, &rslt, tottimeout) != RPC_SUCCESS) { clnt_perror(client, "Cannot register service"); return (FALSE); } CLNT_DESTROY(client); (void)close(socket); return (rslt); }
/* * Remove the mapping between program,version and port. * Calls the pmap service remotely to do the un-mapping. */ bool_t pmap_unset( rpcprog_t program, rpcvers_t version) { struct sockaddr_in myaddress; int socket = -1; register CLIENT *client; struct pmap parms; bool_t rslt; get_myaddress(&myaddress); client = clntudp_bufcreate(&myaddress, PMAPPROG, PMAPVERS, timeout, &socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); if (client == (CLIENT *)NULL) return (FALSE); parms.pm_prog = program; parms.pm_vers = version; parms.pm_port = parms.pm_prot = 0; CLNT_CALL(client, PMAPPROC_UNSET, xdr_pmap, &parms, xdr_bool, &rslt, tottimeout); CLNT_DESTROY(client); (void)close(socket); return (rslt); }
/* * Remove the mapping between program,version and port. * Calls the pmap service remotely to do the un-mapping. */ bool_t pmap_unset( unsigned long program, unsigned long version) { struct sockaddr_in myaddress; int sockfd = -1; register CLIENT *client; struct pmap parms; bool_t rslt; get_myaddress(&myaddress); client = clnttcp_create(&myaddress, PMAPPROG, PMAPVERS, &sockfd, 0, 0); if (client == (CLIENT *)NULL) return (FALSE); parms.pm_prog = program; parms.pm_vers = version; parms.pm_port = parms.pm_prot = 0; if (CLNT_CALL(client, PMAPPROC_UNSET, xdr_pmap, &parms, xdr_bool, &rslt, tottimeout) != RPC_SUCCESS) { clnt_perror(client, "pmap_unset: Cannot unregister service"); rslt = FALSE; } CLNT_DESTROY(client); return (rslt); }
/* * Find the mapped port for program,version. * Calls the pmap service remotely to do the lookup. * Returns 0 if no map exists. */ u_short pmap_getport( struct sockaddr_in *address, rpcprog_t program, rpcvers_t version, rpcprot_t protocol) { unsigned short port = 0; int sock = -1; register CLIENT *client; struct pmap parms; address->sin_port = htons(PMAPPORT); client = clntudp_bufcreate(address, PMAPPROG, PMAPVERS, timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); if (client != (CLIENT *)NULL) { parms.pm_prog = program; parms.pm_vers = version; parms.pm_prot = protocol; parms.pm_port = 0; /* not needed or used */ if (CLNT_CALL(client, PMAPPROC_GETPORT, xdr_pmap, &parms, xdr_u_short, &port, tottimeout) != RPC_SUCCESS) { rpc_createerr.cf_stat = RPC_PMAPFAILURE; clnt_geterr(client, &rpc_createerr.cf_error); } else if (port == 0) { rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; } CLNT_DESTROY(client); } (void)close(sock); address->sin_port = 0; return (port); }
/* * pmapper remote-call-service interface. * This routine is used to call the pmapper remote call service * which will look up a service program in the port maps, and then * remotely call that routine with the given parameters. This allows * programs to do a lookup and call in one step. */ enum clnt_stat pmap_rmtcall( struct sockaddr_in *addr, rpcprog_t prog, rpcvers_t vers, rpcproc_t proc, xdrproc_t xdrargs, caddr_t argsp, xdrproc_t xdrres, caddr_t resp, struct timeval tout, rpcport_t *port_ptr) { SOCKET sock = INVALID_SOCKET; CLIENT *client; struct rmtcallargs a; struct rmtcallres r; enum clnt_stat stat; addr->sin_port = htons(PMAPPORT); client = clntudp_create(addr, PMAPPROG, PMAPVERS, timeout, &sock); if (client != (CLIENT *)NULL) { a.prog = prog; a.vers = vers; a.proc = proc; a.args_ptr = argsp; a.xdr_args = xdrargs; r.port_ptr = port_ptr; r.results_ptr = resp; r.xdr_results = xdrres; stat = CLNT_CALL(client, PMAPPROC_CALLIT, xdr_rmtcall_args, &a, xdr_rmtcallres, &r, tout); CLNT_DESTROY(client); } else { stat = RPC_FAILED; } (void)closesocket(sock); addr->sin_port = 0; return (stat); }
/* * RPC calls to the keyserv. * * If (use_ruid == 1), use real uid. * If (use_ruid == 0), use effective uid. * Returns 0 on failure, 1 on success */ int key_call_ext(rpcproc_t proc, xdrproc_t xdr_arg, char *arg, xdrproc_t xdr_rslt, char *rslt, int use_ruid) { CLIENT *clnt; struct timeval wait_time = {0, 0}; enum clnt_stat status; int vers; if (proc == KEY_ENCRYPT_PK && __key_encryptsession_pk_LOCAL) { cryptkeyres res; bool_t r; r = (*__key_encryptsession_pk_LOCAL)(geteuid(), arg, &res); if (r == TRUE) { /* LINTED pointer alignment */ *(cryptkeyres*)rslt = res; return (1); } return (0); } if (proc == KEY_DECRYPT_PK && __key_decryptsession_pk_LOCAL) { cryptkeyres res; bool_t r; r = (*__key_decryptsession_pk_LOCAL)(geteuid(), arg, &res); if (r == TRUE) { /* LINTED pointer alignment */ *(cryptkeyres*)rslt = res; return (1); } return (0); } if (proc == KEY_GEN && __key_gendes_LOCAL) { des_block res; bool_t r; r = (*__key_gendes_LOCAL)(geteuid(), 0, &res); if (r == TRUE) { /* LINTED pointer alignment */ *(des_block*)rslt = res; return (1); } return (0); } if ((proc == KEY_ENCRYPT_PK) || (proc == KEY_DECRYPT_PK) || (proc == KEY_NET_GET) || (proc == KEY_NET_PUT) || (proc == KEY_GET_CONV)) vers = 2; /* talk to version 2 */ else vers = 1; /* talk to version 1 */ clnt = getkeyserv_handle(vers, 0); if (clnt == NULL) return (0); auth_destroy(clnt->cl_auth); if (use_ruid) clnt->cl_auth = authsys_create_ruid(); else clnt->cl_auth = authnone_create(); status = CLNT_CALL(clnt, proc, xdr_arg, arg, xdr_rslt, rslt, wait_time); switch (status) { case RPC_SUCCESS: return (1); case RPC_CANTRECV: /* * keyserv was probably restarted, so we'll try once more */ if ((clnt = getkeyserv_handle(vers, 1)) == NULL) return (0); auth_destroy(clnt->cl_auth); if (use_ruid) clnt->cl_auth = authsys_create_ruid(); else clnt->cl_auth = authnone_create(); if (CLNT_CALL(clnt, proc, xdr_arg, arg, xdr_rslt, rslt, wait_time) == RPC_SUCCESS) return (1); return (0); default: return (0); } }
enum clnt_stat rpcbind_getaddr(struct knetconfig *config, rpcprog_t prog, rpcvers_t vers, struct netbuf *addr) { char *ua = NULL; enum clnt_stat status; RPCB parms; struct timeval tmo; CLIENT *client = NULL; k_sigset_t oldmask; k_sigset_t newmask; ushort_t port; int iptype; /* * Call rpcbind (local or remote) to get an address we can use * in an RPC client handle. */ tmo.tv_sec = RPC_PMAP_TIMEOUT; tmo.tv_usec = 0; parms.r_prog = prog; parms.r_vers = vers; parms.r_addr = parms.r_owner = ""; if (strcmp(config->knc_protofmly, NC_INET) == 0) { if (strcmp(config->knc_proto, NC_TCP) == 0) parms.r_netid = "tcp"; else parms.r_netid = "udp"; put_inet_port(addr, htons(PMAPPORT)); } else if (strcmp(config->knc_protofmly, NC_INET6) == 0) { if (strcmp(config->knc_proto, NC_TCP) == 0) parms.r_netid = "tcp6"; else parms.r_netid = "udp6"; put_inet6_port(addr, htons(PMAPPORT)); } else if (strcmp(config->knc_protofmly, NC_LOOPBACK) == 0) { ASSERT(strnrchr(addr->buf, '.', addr->len) != NULL); if (config->knc_semantics == NC_TPI_COTS_ORD) parms.r_netid = "ticotsord"; else if (config->knc_semantics == NC_TPI_COTS) parms.r_netid = "ticots"; else parms.r_netid = "ticlts"; put_loopback_port(addr, "rpc"); } else { status = RPC_UNKNOWNPROTO; goto out; } /* * Mask signals for the duration of the handle creation and * RPC calls. This allows relatively normal operation with a * signal already posted to our thread (e.g., when we are * sending an NLM_CANCEL in response to catching a signal). * * Any further exit paths from this routine must restore * the original signal mask. */ sigfillset(&newmask); sigreplace(&newmask, &oldmask); if (clnt_tli_kcreate(config, addr, RPCBPROG, RPCBVERS, 0, 0, CRED(), &client)) { status = RPC_TLIERROR; sigreplace(&oldmask, (k_sigset_t *)NULL); goto out; } client->cl_nosignal = 1; if ((status = CLNT_CALL(client, RPCBPROC_GETADDR, xdr_rpcb, (char *)&parms, xdr_wrapstring, (char *)&ua, tmo)) != RPC_SUCCESS) { sigreplace(&oldmask, (k_sigset_t *)NULL); goto out; } sigreplace(&oldmask, (k_sigset_t *)NULL); if (ua == NULL || *ua == NULL) { status = RPC_PROGNOTREGISTERED; goto out; } /* * Convert the universal address to the transport address. * Theoretically, we should call the local rpcbind to translate * from the universal address to the transport address, but it gets * complicated (e.g., there's no direct way to tell rpcbind that we * want an IP address instead of a loopback address). Note that * the transport address is potentially host-specific, so we can't * just ask the remote rpcbind, because it might give us the wrong * answer. */ if (strcmp(config->knc_protofmly, NC_INET) == 0) { /* make sure that the ip address is the correct type */ if (rpc_iptype(ua, &iptype) != 0) { status = RPC_UNKNOWNADDR; goto out; } port = rpc_uaddr2port(iptype, ua); put_inet_port(addr, ntohs(port)); } else if (strcmp(config->knc_protofmly, NC_INET6) == 0) { /* make sure that the ip address is the correct type */ if (rpc_iptype(ua, &iptype) != 0) { status = RPC_UNKNOWNADDR; goto out; } port = rpc_uaddr2port(iptype, ua); put_inet6_port(addr, ntohs(port)); } else if (strcmp(config->knc_protofmly, NC_LOOPBACK) == 0) { loopb_u2t(ua, addr); } else { /* "can't happen" - should have been checked for above */ cmn_err(CE_PANIC, "rpcbind_getaddr: bad protocol family"); } out: if (client != NULL) { auth_destroy(client->cl_auth); clnt_destroy(client); } if (ua != NULL) xdr_free(xdr_wrapstring, (char *)&ua); return (status); }
int nfs3getdents(struct nfs_file *nfp, struct dirent *dep, unsigned size) { int cnt = 0; entry3 *ep; READDIR3args rda; READDIR3res res; enum clnt_stat status; struct { entry3 etlist[MAXDENTS]; char names[MAXDENTS][NFS_MAXNAMLEN+1]; } rdbuf; int j; struct timeval zero_timeout = {0, 0}; /* default */ bzero((caddr_t)&res, sizeof (res)); bzero((caddr_t)&rda, sizeof (rda)); bzero((caddr_t)rdbuf.etlist, sizeof (rdbuf.etlist)); rda.dir.data.data_len = nfp->fh.fh3.len; rda.dir.data.data_val = nfp->fh.fh3.data; rda.cookie = nfp->cookie.cookie3; while (!res.READDIR3res_u.resok.reply.eof) { /* * Keep issuing nfs calls until EOF is reached on * the directory or the user buffer is filled. */ for (j = 0; j < MAXDENTS; j++) { /* * Link our buffers together for the benefit of * XDR. We do this each time we issue the rpc call * JIC the xdr decode * routines screw up the linkage! */ rdbuf.etlist[j].name = rdbuf.names[(MAXDENTS-1) - j]; rdbuf.etlist[j].nextentry = (j < (MAXDENTS-1)) ? &rdbuf.etlist[j+1] : 0; } res.READDIR3res_u.resok.reply.entries = rdbuf.etlist; /* * Cannot give the whole buffer unless every name is * 256 bytes! Assume the worst case of all 1 byte names. * This results in MINSIZ bytes/name in the xdr stream. */ rda.count = sizeof (res) + MAXDENTS*MINSIZ; bzero((caddr_t)rdbuf.names, sizeof (rdbuf.names)); status = CLNT_CALL(root_CLIENT, NFSPROC3_READDIR, xdr_READDIR3args, (caddr_t)&rda, xdr_READDIR3res, (caddr_t)&res, zero_timeout); if (status != RPC_SUCCESS) { dprintf("nfs3_getdents: RPC error\n"); return (-1); } if (res.status != NFS3_OK) { /* * The most common failure here would be trying to * issue a getdents call on a non-directory! */ nfs3_error(res.status); return (-1); } for (ep = rdbuf.etlist; ep; ep = ep->nextentry) { /* * Step thru all entries returned by NFS, converting * to the cannonical form and copying out to the * user's buffer. */ int n; /* * catch the case user called at EOF */ if ((n = strlen(ep->name)) == 0) return (cnt); n = BDIRENT_RECLEN(n); if (n > size) return (cnt); size -= n; (void) strcpy(dep->d_name, ep->name); dep->d_ino = ep->fileid; dep->d_off = (off_t)ep->cookie; dep->d_reclen = (ushort_t)n; dep = (struct dirent *)((char *)dep + n); rda.cookie = ep->cookie; nfp->cookie.cookie3 = ep->cookie; cnt++; } } return (cnt); }
/* * This is the simplified interface to the client rpc layer. * The client handle is not destroyed here and is reused for * the future calls to same prog, vers, host and nettype combination. * * The total time available is 25 seconds. */ enum clnt_stat rpc_call(const char *host, /* host name */ rpcprog_t prognum, /* program number */ rpcvers_t versnum, /* version number */ rpcproc_t procnum, /* procedure number */ xdrproc_t inproc, const char *in, xdrproc_t outproc, /* in/out XDR procedures */ char *out, /* recv/send data */ const char *nettype) /* nettype */ { struct rpc_call_private *rcp = NULL; enum clnt_stat clnt_stat; struct timeval timeout, tottimeout; static thread_key_t rpc_call_key; int main_thread = 1; if ((main_thread = thr_main())) { rcp = rpc_call_private_main; } else { if (rpc_call_key == 0) { mutex_lock(&tsd_lock); if (rpc_call_key == 0) thr_keycreate(&rpc_call_key, rpc_call_destroy); mutex_unlock(&tsd_lock); } rcp = (struct rpc_call_private *)thr_getspecific(rpc_call_key); } if (rcp == NULL) { rcp = malloc(sizeof (*rcp)); if (rcp == NULL) { rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; return (rpc_createerr.cf_stat); } if (main_thread) rpc_call_private_main = rcp; else thr_setspecific(rpc_call_key, (void *) rcp); rcp->valid = 0; rcp->client = NULL; } if ((nettype == NULL) || (nettype[0] == 0)) nettype = "netpath"; if (!(rcp->valid && rcp->pid == getpid() && (rcp->prognum == prognum) && (rcp->versnum == versnum) && (!strcmp(rcp->host, host)) && (!strcmp(rcp->nettype, nettype)))) { int fd; rcp->valid = 0; if (rcp->client) CLNT_DESTROY(rcp->client); /* * Using the first successful transport for that type */ rcp->client = clnt_create(host, prognum, versnum, nettype); rcp->pid = getpid(); if (rcp->client == NULL) { return (rpc_createerr.cf_stat); } /* * Set time outs for connectionless case. Do it * unconditionally. Faster than doing a t_getinfo() * and then doing the right thing. */ timeout.tv_usec = 0; timeout.tv_sec = 5; CLNT_CONTROL(rcp->client, CLSET_RETRY_TIMEOUT, (char *)(void *)&timeout); if (CLNT_CONTROL(rcp->client, CLGET_FD, (char *)(void *)&fd)) _fcntl(fd, F_SETFD, 1); /* make it "close on exec" */ rcp->prognum = prognum; rcp->versnum = versnum; if ((strlen(host) < (size_t)MAXHOSTNAMELEN) && (strlen(nettype) < (size_t)NETIDLEN)) { strcpy(rcp->host, host); strcpy(rcp->nettype, nettype); rcp->valid = 1; } else { rcp->valid = 0; } } /* else reuse old client */ tottimeout.tv_sec = 25; tottimeout.tv_usec = 0; /*LINTED const castaway*/ clnt_stat = CLNT_CALL(rcp->client, procnum, inproc, (char *) in, outproc, out, tottimeout); /* * if call failed, empty cache */ if (clnt_stat != RPC_SUCCESS) rcp->valid = 0; return (clnt_stat); }
static void test_client(int argc, const char **argv) { rpcproc_t prog = 123456; rpcvers_t vers = 1; const char *netid = "tcp"; char hostname[128], service[128+5]; CLIENT *client; AUTH *auth; const char **mechs; rpc_gss_options_req_t options_req; rpc_gss_options_ret_t options_ret; rpc_gss_service_t svc; struct timeval tv; enum clnt_stat stat; if (argc == 2) strlcpy(hostname, argv[1], sizeof(hostname)); else gethostname(hostname, sizeof(hostname)); client = clnt_create(hostname, prog, vers, netid); if (!client) { printf("rpc_createerr.cf_stat = %d\n", rpc_createerr.cf_stat); printf("rpc_createerr.cf_error.re_errno = %d\n", rpc_createerr.cf_error.re_errno); return; } strcpy(service, "host"); strcat(service, "@"); strcat(service, hostname); mechs = rpc_gss_get_mechanisms(); auth = NULL; while (*mechs) { options_req.req_flags = GSS_C_MUTUAL_FLAG; options_req.time_req = 600; options_req.my_cred = GSS_C_NO_CREDENTIAL; options_req.input_channel_bindings = NULL; auth = rpc_gss_seccreate(client, service, *mechs, rpc_gss_svc_none, NULL, &options_req, &options_ret); if (auth) break; mechs++; } if (!auth) { clnt_pcreateerror("rpc_gss_seccreate"); printf("Can't authenticate with server %s.\n", hostname); exit(1); } client->cl_auth = auth; for (svc = rpc_gss_svc_none; svc <= rpc_gss_svc_privacy; svc++) { const char *svc_names[] = { "rpc_gss_svc_default", "rpc_gss_svc_none", "rpc_gss_svc_integrity", "rpc_gss_svc_privacy" }; int num; rpc_gss_set_defaults(auth, svc, NULL); tv.tv_sec = 5; tv.tv_usec = 0; num = 42; stat = CLNT_CALL(client, 1, (xdrproc_t) xdr_int, (char *) &num, (xdrproc_t) xdr_int, (char *) &num, tv); if (stat == RPC_SUCCESS) { printf("succeeded with %s\n", svc_names[svc]); if (num != 142) printf("unexpected reply %d\n", num); } else { clnt_perror(client, "call failed"); } } AUTH_DESTROY(auth); CLNT_DESTROY(client); }
/* * This routine will open a device as it is known by the V2 OBP. It * then goes thru the stuff necessary to initialize the network device, * get our network parameters, (using DHCP or rarp/bootparams), and * finally actually go and get the root filehandle. Sound like fun? * Suuurrrree. Take a look. * * Returns 0 if things worked. -1 if we crashed and burned. */ int boot_nfs_mountroot(char *str) { int status; enum clnt_stat rpc_stat; char *root_path = &root_pathbuf[0]; /* to make XDR happy */ struct timeval wait; int fd; int bufsize; char *opts, *val; int nfs_version = 0; int istcp = 1; int nfs_port = 0; /* Cause pmap to get port */ struct sockaddr_in tmp_addr; /* throw away */ if (root_CLIENT != NULL) { AUTH_DESTROY(root_CLIENT->cl_auth); CLNT_DESTROY(root_CLIENT); root_CLIENT = NULL; } root_to.sin_family = AF_INET; root_to.sin_addr.s_addr = htonl(INADDR_ANY); root_to.sin_port = htons(0); mac_init(str); (void) ipv4_setpromiscuous(TRUE); if (get_netconfig_strategy() == NCT_BOOTP_DHCP) { if (boothowto & RB_VERBOSE) printf("Using BOOTP/DHCP...\n"); if (dhcp() != 0 || setup_root_vars() != 0) { (void) ipv4_setpromiscuous(FALSE); if (boothowto & RB_VERBOSE) printf("BOOTP/DHCP configuration failed!\n"); return (-1); } /* now that we have an IP address, turn off promiscuous mode */ (void) ipv4_setpromiscuous(FALSE); } else { /* Use RARP/BOOTPARAMS. RARP will try forever... */ if (boothowto & RB_VERBOSE) printf("Using RARP/BOOTPARAMS...\n"); mac_call_rarp(); /* * Since there is no way to determine our netmask, and therefore * figure out if the router we got is useful, we assume all * services are local. Use DHCP if this bothers you. */ dontroute = TRUE; /* * We are trying to keep the ARP response * timeout on the lower side with BOOTP/RARP. * We are doing this for BOOTP/RARP where policy * doesn't allow to route the packets outside * the subnet as it has no idea about the * netmask. By doing so, we are reducing * ARP response timeout for any packet destined * for outside booting clients subnet. Client can * not expect such ARP replies and will finally * timeout after a long delay. This would cause * booting client to get stalled for a longer * time. We can not avoid accepting any outside * subnet packets accidentally destined for the * booting client. */ mac_set_arp_timeout(ARP_INETBOOT_TIMEOUT); /* now that we have an IP address, turn off promiscuous mode */ (void) ipv4_setpromiscuous(FALSE); /* get our hostname */ if (whoami() == FALSE) return (-1); /* get our bootparams. */ if (getfile("root", root_hostname, &root_to.sin_addr, root_pathbuf) == FALSE) return (-1); /* get our rootopts. */ (void) getfile("rootopts", root_hostname, &tmp_addr.sin_addr, rootopts); } /* mount root */ if (boothowto & RB_VERBOSE) { printf("root server: %s (%s)\n", root_hostname, inet_ntoa(root_to.sin_addr)); printf("root directory: %s\n", root_pathbuf); } /* * Assumes we've configured the stack and thus know our * IP address/hostname, either by using DHCP or rarp/bootparams. */ gethostname(my_hostname, sizeof (my_hostname)); wait.tv_sec = RPC_RCVWAIT_MSEC / 1000; wait.tv_usec = 0; /* * Parse out the interesting root options, if an invalid * or unknown option is provided, silently ignore it and * use the defaults. */ opts = rootopts; while (*opts) { int ival; switch (getsubopt(&opts, optlist, &val)) { case OPT_RSIZE: if (val == NULL || !isdigit(*val)) break; nfs_readsize = atoi(val); break; case OPT_TIMEO: if (val == NULL || !isdigit(*val)) break; ival = atoi(val); wait.tv_sec = ival / 10; wait.tv_usec = (ival % 10) * 100000; break; case OPT_VERS: if (val == NULL || !isdigit(*val)) break; nfs_version = atoi(val); break; case OPT_PROTO: if (val == NULL || isdigit(*val)) break; if ((strncmp(val, "udp", 3) == 0)) istcp = 0; else istcp = 1; /* must be tcp */ break; case OPT_PORT: if (val == NULL || !isdigit(*val)) break; nfs_port = atoi(val); /* * Currently nfs_dlinet.c doesn't support setting * the root NFS port. Delete this when it does. */ nfs_port = 0; break; default: /* * Unknown options are silently ignored */ break; } } /* * If version is set, then try that version first. */ switch (nfs_version) { case NFS_VERSION: if (nfsmountroot(root_path, &roothandle) == 0) goto domount; break; case NFS_V3: if (nfs3mountroot(root_path, &roothandle) == 0) goto domount; break; case NFS_V4: /* * With v4 we skip the mount and go straight to * setting the root filehandle. Because of this we * do things slightly differently and obtain our * client handle first. */ if (istcp && nfs4init(root_path, nfs_port) == 0) { /* * If v4 init succeeded then we are done. Just return. */ return (0); } } /* * If there was no chosen version or the chosen version failed * try all versions in order, this may still fail to boot * at the kernel level if the options are not right, but be * generous at this early stage. */ if (istcp && nfs4init(root_path, nfs_port) == 0) { /* * If v4 init succeeded then we are done. Just return. */ return (0); } if (nfs3mountroot(root_path, &roothandle) == 0) goto domount; if ((status = nfsmountroot(root_path, &roothandle)) != 0) return (status); domount: /* * Only v2 and v3 go on from here. */ roothandle.offset = (uint_t)0; /* it's a directory! */ root_to.sin_port = htons(nfs_port); /* NFS is next after mount */ /* * Create the CLIENT handle for NFS operations */ if (roothandle.version == NFS_VERSION) bufsize = NFSBUF_SIZE; else bufsize = NFS3BUF_SIZE; /* * First try TCP then UDP (unless UDP asked for explicitly), if mountd * alows this version but neither transport is available we are stuck. */ if (istcp) { fd = -1; root_CLIENT = clntbtcp_create(&root_to, NFS_PROGRAM, roothandle.version, wait, &fd, bufsize, bufsize); if (root_CLIENT != NULL) { root_CLIENT->cl_auth = authunix_create(my_hostname, 0, 1, 1, &fake_gids); /* * Send NULL proc, check if the server really exists */ rpc_stat = CLNT_CALL(root_CLIENT, 0, xdr_void, NULL, xdr_void, NULL, wait); if (rpc_stat == RPC_SUCCESS) return (0); AUTH_DESTROY(root_CLIENT->cl_auth); CLNT_DESTROY(root_CLIENT); root_CLIENT = NULL; } /* Fall through to UDP case */ } fd = -1; root_CLIENT = clntbudp_bufcreate(&root_to, NFS_PROGRAM, roothandle.version, wait, &fd, bufsize, bufsize); if (root_CLIENT == NULL) return (-1); root_CLIENT->cl_auth = authunix_create(my_hostname, 0, 1, 1, &fake_gids); /* * Send NULL proc, check if the server really exists */ rpc_stat = CLNT_CALL(root_CLIENT, 0, xdr_void, NULL, xdr_void, NULL, wait); if (rpc_stat == RPC_SUCCESS) return (0); AUTH_DESTROY(root_CLIENT->cl_auth); CLNT_DESTROY(root_CLIENT); root_CLIENT = NULL; return (-1); }
static int nfs_probe_statd(void) { struct sockaddr_in addr = { .sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK), }; rpcprog_t program = nfs_getrpcbyname(NSMPROG, nfs_ns_pgmtbl); return nfs_getport_ping((struct sockaddr *)&addr, sizeof(addr), program, (rpcvers_t)1, IPPROTO_UDP); } /** * start_statd - attempt to start rpc.statd * * Returns 1 if statd is running; otherwise zero. */ int start_statd(void) { #ifdef START_STATD struct stat stb; #endif if (nfs_probe_statd()) return 1; #ifdef START_STATD if (stat(START_STATD, &stb) == 0) { if (S_ISREG(stb.st_mode) && (stb.st_mode & S_IXUSR)) { pid_t pid = fork(); switch (pid) { case 0: /* child */ execl(START_STATD, START_STATD, NULL); exit(1); case -1: /* error */ nfs_error(_("fork failed: %s"), strerror(errno)); break; default: /* parent */ waitpid(pid, NULL,0); break; } if (nfs_probe_statd()) return 1; } } #endif return 0; } /** * nfs_advise_umount - ask the server to remove a share from it's rmtab * @sap: pointer to IP address of server to call * @salen: length of server address * @pmap: partially filled-in mountd RPC service tuple * @argp: directory path of share to "unmount" * * Returns one if the unmount call succeeded; zero if the unmount * failed for any reason; rpccreateerr.cf_stat is set to reflect * the nature of the error. * * We use a fast timeout since this call is advisory only. */ int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen, const struct pmap *pmap, const dirpath *argp) { struct sockaddr_storage address; struct sockaddr *saddr = (struct sockaddr *)&address; struct pmap mnt_pmap = *pmap; struct timeval timeout = { .tv_sec = MOUNT_TIMEOUT >> 3, }; CLIENT *client; enum clnt_stat res = 0; if (nfs_probe_mntport(sap, salen, &mnt_pmap) == 0) return 0; memcpy(saddr, sap, salen); nfs_set_port(saddr, mnt_pmap.pm_port); client = nfs_get_rpcclient(saddr, salen, mnt_pmap.pm_prot, mnt_pmap.pm_prog, mnt_pmap.pm_vers, &timeout); if (client == NULL) return 0; client->cl_auth = authunix_create_default(); res = CLNT_CALL(client, MOUNTPROC_UMNT, (xdrproc_t)xdr_dirpath, (caddr_t)argp, (xdrproc_t)xdr_void, NULL, timeout); auth_destroy(client->cl_auth); CLNT_DESTROY(client); if (res != RPC_SUCCESS) return 0; return 1; } /** * nfs_call_umount - ask the server to remove a share from it's rmtab * @mnt_server: address of RPC MNT program server * @argp: directory path of share to "unmount" * * Returns one if the unmount call succeeded; zero if the unmount * failed for any reason. * * Note that a side effect of calling this function is that rpccreateerr * is set. */ int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp) { struct sockaddr *sap = (struct sockaddr *)&mnt_server->saddr; socklen_t salen = sizeof(mnt_server->saddr); struct pmap *pmap = &mnt_server->pmap; CLIENT *clnt; enum clnt_stat res = 0; int msock; if (!nfs_probe_mntport(sap, salen, pmap)) return 0; clnt = mnt_openclnt(mnt_server, &msock); if (!clnt) return 0; res = clnt_call(clnt, MOUNTPROC_UMNT, (xdrproc_t)xdr_dirpath, (caddr_t)argp, (xdrproc_t)xdr_void, NULL, TIMEOUT); mnt_closeclnt(clnt, msock); if (res == RPC_SUCCESS) return 1; return 0; } /** * mnt_openclnt - get a handle for a remote mountd service * @mnt_server: address and pmap arguments of mountd service * @msock: returns a file descriptor of the underlying transport socket * * Returns an active handle for the remote's mountd service */ CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock) { struct sockaddr_in *mnt_saddr = &mnt_server->saddr; struct pmap *mnt_pmap = &mnt_server->pmap; CLIENT *clnt = NULL; mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port); *msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, MOUNT_TIMEOUT, TRUE, FALSE); if (*msock == RPC_ANYSOCK) { if (rpc_createerr.cf_error.re_errno == EADDRINUSE) /* * Probably in-use by a TIME_WAIT connection, * It is worth waiting a while and trying again. */ rpc_createerr.cf_stat = RPC_TIMEDOUT; return NULL; } switch (mnt_pmap->pm_prot) { case IPPROTO_UDP: clnt = clntudp_bufcreate(mnt_saddr, mnt_pmap->pm_prog, mnt_pmap->pm_vers, RETRY_TIMEOUT, msock, MNT_SENDBUFSIZE, MNT_RECVBUFSIZE); break; case IPPROTO_TCP: clnt = clnttcp_create(mnt_saddr, mnt_pmap->pm_prog, mnt_pmap->pm_vers, msock, MNT_SENDBUFSIZE, MNT_RECVBUFSIZE); break; } if (clnt) { /* try to mount hostname:dirname */ clnt->cl_auth = authunix_create_default(); return clnt; } return NULL; } /** * mnt_closeclnt - terminate a handle for a remote mountd service * @clnt: pointer to an active handle for a remote mountd service * @msock: file descriptor of the underlying transport socket * */ void mnt_closeclnt(CLIENT *clnt, int msock) { auth_destroy(clnt->cl_auth); clnt_destroy(clnt); close(msock); } /** * clnt_ping - send an RPC ping to the remote RPC service endpoint * @saddr: server's address * @prog: target RPC program number * @vers: target RPC version number * @prot: target RPC protocol * @caddr: filled in with our network address * * Sigh... GETPORT queries don't actually check the version number. * In order to make sure that the server actually supports the service * we're requesting, we open an RPC client, and fire off a NULL * RPC call. * * caddr is the network address that the server will use to call us back. * On multi-homed clients, this address depends on which NIC we use to * route requests to the server. * * Returns one if successful, otherwise zero. */ int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog, const unsigned long vers, const unsigned int prot, struct sockaddr_in *caddr) { CLIENT *clnt = NULL; int sock, stat; static char clnt_res; struct sockaddr dissolve; rpc_createerr.cf_stat = stat = 0; sock = get_socket(saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE); if (sock == RPC_ANYSOCK) { if (rpc_createerr.cf_error.re_errno == ETIMEDOUT) { /* * TCP timeout. Bubble up the error to see * how it should be handled. */ rpc_createerr.cf_stat = RPC_TIMEDOUT; } return 0; } if (caddr) { /* Get the address of our end of this connection */ socklen_t len = sizeof(*caddr); if (getsockname(sock, caddr, &len) != 0) caddr->sin_family = 0; } switch(prot) { case IPPROTO_UDP: /* The socket is connected (so we could getsockname successfully), * but some servers on multi-homed hosts reply from * the wrong address, so if we stay connected, we lose the reply. */ dissolve.sa_family = AF_UNSPEC; connect(sock, &dissolve, sizeof(dissolve)); clnt = clntudp_bufcreate(saddr, prog, vers, RETRY_TIMEOUT, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); break; case IPPROTO_TCP: clnt = clnttcp_create(saddr, prog, vers, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); break; } if (!clnt) { close(sock); return 0; } memset(&clnt_res, 0, sizeof(clnt_res)); stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void, (caddr_t)NULL, (xdrproc_t)xdr_void, (caddr_t)&clnt_res, TIMEOUT); if (stat) { clnt_geterr(clnt, &rpc_createerr.cf_error); rpc_createerr.cf_stat = stat; } clnt_destroy(clnt); close(sock); if (stat == RPC_SUCCESS) return 1; else return 0; }
/* * Setup v4 client for inetboot */ static int nfs4init(char *path, uint16_t nfs_port) { struct timeval wait; int fd = -1; int error = 0; enum clnt_stat rpc_stat; struct nfs_file rootpath; wait.tv_sec = RPC_RCVWAIT_MSEC / 1000; wait.tv_usec = 0; /* * If we haven't explicitly set the port number, set to the standard * 2049 and don't cause a rpcbind request. */ if (nfs_port == 0) nfs_port = 2049; root_to.sin_port = htons(nfs_port); /* * Support TCP only */ root_CLIENT = clntbtcp_create(&root_to, NFS_PROGRAM, NFS_V4, wait, &fd, NFS4BUF_SIZE, NFS4BUF_SIZE); if (root_CLIENT == NULL) { root_to.sin_port = 0; return (-1); } root_CLIENT->cl_auth = authunix_create(my_hostname, 0, 1, 1, &fake_gids); /* * Send NULL proc the server first to see if V4 exists */ rpc_stat = CLNT_CALL(root_CLIENT, NFSPROC4_NULL, xdr_void, NULL, xdr_void, NULL, wait); if (rpc_stat != RPC_SUCCESS) { dprintf("boot: NULL proc failed NFSv4 service not available\n"); AUTH_DESTROY(root_CLIENT->cl_auth); CLNT_DESTROY(root_CLIENT); root_to.sin_port = 0; return (-1); } /* * Do a lookup to get to the root_path. This is nice since it can * handle multicomponent lookups. */ roothandle.version = NFS_V4; roothandle.ftype.type4 = NF4DIR; roothandle.fh.fh4.len = 0; /* Force a PUTROOTFH */ roothandle.offset = (uint_t)0; /* it's a directory! */ error = lookup(path, &rootpath, TRUE); if (error) { printf("boot: lookup %s failed\n", path); return (-1); } roothandle = rootpath; /* structure copy */ /* * Hardwire in a known reasonable upper limit of 32K */ nfs_readsize = nfs_readsize < 32 * 1024 ? nfs_readsize : 32 * 1024; /* * Set a reasonable lower limit on readsize */ nfs_readsize = (nfs_readsize != 0 && nfs_readsize < 512) ? 512 : nfs_readsize; return (0); }
/* * This is the simplified interface to the client rpc layer. * The client handle is not destroyed here and is reused for * the future calls to same prog, vers, host and nettype combination. * * The total time available is 25 seconds. */ enum clnt_stat rpc_call(const char *host, const rpcprog_t prognum, const rpcvers_t versnum, const rpcproc_t procnum, const xdrproc_t inproc, const char *in, const xdrproc_t outproc, char *out, const char *netclass) { struct rpc_call_private *rcp; enum clnt_stat clnt_stat; struct timeval timeout, tottimeout; static pthread_key_t rpc_call_key = PTHREAD_ONCE_KEY_NP; char nettype_array[NETIDLEN]; char *nettype = &nettype_array[0]; if (netclass == NULL) nettype = NULL; else { size_t len = strlen(netclass); if (len >= sizeof (nettype_array)) { rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; return (rpc_createerr.cf_stat); } (void) strcpy(nettype, netclass); } rcp = thr_get_storage(&rpc_call_key, sizeof (*rcp), rpc_call_destroy); if (rcp == NULL) { rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; return (rpc_createerr.cf_stat); } if ((nettype == NULL) || (nettype[0] == NULL)) nettype = "netpath"; if (!(rcp->valid && rcp->pid == getpid() && rcp->prognum == prognum && rcp->versnum == versnum && strcmp(rcp->host, host) == 0 && strcmp(rcp->nettype, nettype) == 0)) { int fd; rcp->valid = 0; if (rcp->client) CLNT_DESTROY(rcp->client); /* * Using the first successful transport for that type */ rcp->client = clnt_create(host, prognum, versnum, nettype); rcp->pid = getpid(); if (rcp->client == NULL) return (rpc_createerr.cf_stat); /* * Set time outs for connectionless case. Do it * unconditionally. Faster than doing a t_getinfo() * and then doing the right thing. */ timeout.tv_usec = 0; timeout.tv_sec = 5; (void) CLNT_CONTROL(rcp->client, CLSET_RETRY_TIMEOUT, (char *)&timeout); if (CLNT_CONTROL(rcp->client, CLGET_FD, (char *)&fd)) (void) fcntl(fd, F_SETFD, 1); /* close on exec */ rcp->prognum = prognum; rcp->versnum = versnum; if ((strlen(host) < (size_t)MAXHOSTNAMELEN) && (strlen(nettype) < (size_t)NETIDLEN)) { (void) strcpy(rcp->host, host); (void) strcpy(rcp->nettype, nettype); rcp->valid = 1; } else { rcp->valid = 0; } } /* else reuse old client */ tottimeout.tv_sec = 25; tottimeout.tv_usec = 0; clnt_stat = CLNT_CALL(rcp->client, procnum, inproc, (char *)in, outproc, out, tottimeout); /* * if call failed, empty cache */ if (clnt_stat != RPC_SUCCESS) rcp->valid = 0; return (clnt_stat); }