static struct pmap * get_mountport(struct sockaddr_in *server_addr, long unsigned prog, long unsigned version, long unsigned proto, long unsigned port, int nfs_mount_version) { struct pmaplist *pmap; static struct pmap p = {0, 0, 0, 0}; if (version > MAX_NFSPROT) version = MAX_NFSPROT; if (!prog) prog = MOUNTPROG; p.pm_prog = prog; p.pm_vers = version; p.pm_prot = proto; p.pm_port = port; server_addr->sin_port = PMAPPORT; pmap = pmap_getmaps(server_addr); while (pmap) { if (pmap->pml_map.pm_prog != prog) goto next; if (!version && p.pm_vers > pmap->pml_map.pm_vers) goto next; if (version > 2 && pmap->pml_map.pm_vers != version) goto next; if (version && version <= 2 && pmap->pml_map.pm_vers > 2) goto next; if (pmap->pml_map.pm_vers > MAX_NFSPROT || (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) || (port && pmap->pml_map.pm_port != port)) goto next; memcpy(&p, &pmap->pml_map, sizeof(p)); next: pmap = pmap->pml_next; } if (!p.pm_vers) p.pm_vers = MOUNTVERS; if (!p.pm_prot) p.pm_prot = IPPROTO_TCP; #if 0 if (!p.pm_port) { p.pm_port = pmap_getport(server_addr, p.pm_prog, p.pm_vers, p.pm_prot); } #endif #if 0 #define MOUNTPORT 635 /* HJLu wants to remove all traces of the old default port. Are there still people running a mount RPC service on this port without having a portmapper? */ if (!p.pm_port) p.pm_port = MOUNTPORT; #endif return &p; }
int getrpcport (const char *host, u_long prognum, u_long versnum, u_int proto) { struct sockaddr_in addr; struct hostent hostbuf, *hp; size_t buflen; char *buffer; int herr; buflen = 1024; buffer = __alloca (buflen); while (__gethostbyname_r (host, &hostbuf, buffer, buflen, &hp, &herr) != 0 || hp == NULL) if (herr != NETDB_INTERNAL || errno != ERANGE) return 0; else { /* Enlarge the buffer. */ buflen *= 2; buffer = __alloca (buflen); } memcpy ((char *) &addr.sin_addr, hp->h_addr, hp->h_length); addr.sin_family = AF_INET; addr.sin_port = 0; return pmap_getport (&addr, prognum, versnum, proto); }
/* * Create an rpc client attached to the mount daemon. */ CLIENT * get_mount_client(char *unused_host, struct sockaddr_in *sin, struct timeval *tv, int *sock, u_long mnt_version) { CLIENT *client; /* * First try a TCP socket */ if ((*sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) > 0) { /* * Bind to a privileged port */ if (bind_resv_port(*sock, (u_short *) NULL) < 0) plog(XLOG_ERROR, "can't bind privileged port (socket)"); /* * Find mountd port to connect to. * Connect to mountd. * Create a tcp client. */ if ((sin->sin_port = htons(pmap_getport(sin, MOUNTPROG, mnt_version, IPPROTO_TCP))) != 0) { if (connect(*sock, (struct sockaddr *) sin, sizeof(*sin)) >= 0 && ((client = clnttcp_create(sin, MOUNTPROG, mnt_version, sock, 0, 0)) != NULL)) return client; } /* * Failed so close socket */ (void) close(*sock); } /* tcp socket opened */ /* TCP failed so try UDP */ if ((*sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { plog(XLOG_ERROR, "Can't create socket to connect to mountd: %m"); *sock = RPC_ANYSOCK; return NULL; } /* * Bind to a privileged port */ if (bind_resv_port(*sock, (u_short *) NULL) < 0) plog(XLOG_ERROR, "can't bind privileged port"); /* * Zero out the port - make sure we recompute */ sin->sin_port = 0; /* * Make a UDP client */ if ((client = clntudp_create(sin, MOUNTPROG, mnt_version, *tv, sock)) == NULL) { (void) close(*sock); *sock = RPC_ANYSOCK; return NULL; } dlog("get_mount_client: Using udp, port %d", sin->sin_port); return client; }
struct client * clnttcp_create(struct sockaddr_in *raddr, uint32_t prognum, uint32_t versnum, int *psock, unsigned int sendsz, unsigned int recvsz) { struct client *clnt; struct auth *ath; int sock; struct sockaddr_in sin; assert((raddr != NULL) && (psock != NULL)); clnt = clnt_alloc(), ath = authnone_create(); if ((clnt == NULL) || (ath == NULL)) { LOG_ERROR("clnttcp_create", "clnt_alloc failed"); rpc_create_error.stat = RPC_SYSTEMERROR; rpc_create_error.err.extra.error = ENOMEM; goto exit_with_error; } memcpy(&sin, raddr, sizeof *raddr); if (sin.sin_port == 0) { sin.sin_port = htons(pmap_getport(raddr, prognum, versnum, IPPROTO_TCP)); if (sin.sin_port == 0) { goto exit_with_error; } } assert(*psock < 0); sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if ((sock < 0) || (connect(sock, (struct sockaddr *)&sin, sizeof sin) < 0)) { LOG_ERROR("clnttcp_create", "socket or connect error"); rpc_create_error.stat = RPC_SYSTEMERROR; rpc_create_error.err.extra.error = errno; close(sock); goto exit_with_error; } /* Fill client structure */ clnt->ops = &clnttcp_ops; clnt->sock = sock; clnt->ath = ath; clnt->prognum = prognum; clnt->versnum = versnum; clnt->extra.tcp.sendsz = sendsz; clnt->extra.tcp.recvsz = recvsz; *psock = sock; return clnt; exit_with_error: auth_destroy(ath); clnt_free(clnt); return NULL; }
int main(int argn, char *argc[]) { //Program parameters : argc[1] : HostName or Host IP // argc[2] : Server Program Number // other arguments depend on test case //run_mode can switch into stand alone program or program launch by shell script //1 : stand alone, debug mode, more screen information //0 : launch by shell script as test case, only one printf -> result status int run_mode = 0; int test_status = 1; //Default test result set to FAILED int progNum = atoi(argc[2]); struct hostent *hp = NULL; struct sockaddr_in sin; enum clnt_stat cs; u_long port; struct timeval tv; int var_snd = 10; int var_rec = -1; //Initialization if ((hp = gethostbyname(argc[1])) == (struct hostent *)NULL) { fprintf(stderr, "gethostbyname failed\n"); exit(1); } sin.sin_family = AF_INET; sin.sin_addr.s_addr = *(u_int *)hp->h_addr; port = pmap_getport(&sin, progNum, VERSNUM, IPPROTO_UDP); tv.tv_sec = 0; tv.tv_usec = 100; if (run_mode) { printf("port : %d\n", port); } cs = pmap_rmtcall(&sin, progNum, VERSNUM, PROCNUM, (xdrproc_t)xdr_int, (char *)&var_snd, (xdrproc_t)xdr_int, (char *)&var_rec, tv, &port); test_status = (cs == RPC_SUCCESS) ? 0 : 1; //This last printf gives the result status to the tests suite //normally should be 0: test has passed or 1: test has failed printf("%d\n", test_status); return test_status; }
extern int getrpcport (const char * host, unsigned long prognum, unsigned long versnum, unsigned int proto) { struct sockaddr_in addr; struct hostent *hp; if ((hp = gethostbyname(host)) == NULL) return (0); memmove((char *) &addr.sin_addr, hp->h_addr, hp->h_length); addr.sin_family = AF_INET; addr.sin_port = 0; return (pmap_getport(&addr, prognum, versnum, proto)); }
static int getport (int prog, int version, int protocol) { struct sockaddr_in addr; /* u_short port; */ addr.sin_port = 0; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_family = AF_INET; return pmap_getport (&addr, prog, version, protocol); }
u_short getrpcport(char *host, rpcprog_t prognum, rpcvers_t versnum, rpcprot_t proto) { struct sockaddr_in addr; struct hostent *hp; if ((hp = gethostbyname(host)) == NULL) return (0); memcpy((char *) &addr.sin_addr, hp->h_addr, hp->h_length); addr.sin_family = AF_INET; addr.sin_port = 0; return (pmap_getport(&addr, prognum, versnum, proto)); }
int getrpcport(char *host, int prognum, int versnum, int proto) { struct sockaddr_in addr; struct hostent *hp; if ((hp = gethostbyname(host)) == NULL) return (0); memset(&addr, 0, sizeof(addr)); addr.sin_len = sizeof(struct sockaddr_in); addr.sin_family = AF_INET; addr.sin_port = 0; memcpy((char *)&addr.sin_addr, hp->h_addr, hp->h_length); return (pmap_getport(&addr, prognum, versnum, proto)); }
int main(int argn, char *argc[]) { //Program parameters : argc[1] : HostName or Host IP // argc[2] : Server Program Number // other arguments depend on test case //run_mode can switch into stand alone program or program launch by shell script //1 : stand alone, debug mode, more screen information //0 : launch by shell script as test case, only one printf -> result status int run_mode = 0; int test_status = 1; //Default test result set to FAILED int progNum = atoi(argc[2]); u_int getPort; struct hostent *hp = NULL; struct sockaddr_in sin; //Initialization if ((hp = gethostbyname(argc[1])) == NULL) { fprintf(stderr, "gethostbyname failed\n"); exit(1); } sin.sin_family = AF_INET; sin.sin_addr.s_addr = *(u_int *)hp->h_addr; getPort = pmap_getport(&sin, progNum, VERSNUM, IPPROTO_UDP); if (run_mode) { printf("Port got. %u\n", getPort); printf("Addr. %u\n", sin.sin_addr.s_addr); } test_status = (getPort == 0); //This last printf gives the result status to the tests suite //normally should be 0: test has passed or 1: test has failed printf("%d\n", test_status); return test_status; }
int getrpcport(char *host, int prognum, int versnum, int proto) { struct sockaddr_in addr; struct hostent *hp; assert(host != NULL); if ((hp = gethostbyname(host)) == NULL) return (0); memset(&addr, 0, sizeof(addr)); addr.sin_len = sizeof(struct sockaddr_in); addr.sin_family = AF_INET; addr.sin_port = 0; if (hp->h_length > addr.sin_len) hp->h_length = addr.sin_len; memcpy(&addr.sin_addr.s_addr, hp->h_addr, (size_t)hp->h_length); /* Inconsistent interfaces need casts! :-( */ return (pmap_getport(&addr, (u_long)prognum, (u_long)versnum, (u_int)proto)); }
int gssrpc_getrpcport( char *host, rpcprog_t prognum, rpcvers_t versnum, rpcprot_t proto) { struct sockaddr_in addr; struct hostent *hp; if ((hp = gethostbyname(host)) == NULL) return (0); memset(&addr, 0, sizeof(addr)); memmove((char *) &addr.sin_addr, hp->h_addr, sizeof(addr.sin_addr)); #if HAVE_STRUCT_SOCKADDR_IN_SIN_LEN addr.sin_len = sizeof(addr); #endif addr.sin_family = AF_INET; addr.sin_port = 0; return (pmap_getport(&addr, prognum, versnum, proto)); }
/* Request port mapper to give the port number of a TCP rpc service on a remote host @param host The remote host name @param prog The program number @param vers The version number of the program */ static int get_service_tcp_port(char *host ,unsigned long prog, unsigned long vers) { struct sockaddr_in server; int port = 0; server.sin_family = AF_INET; if (rozofs_host2ip_netw((char*)host, &server.sin_addr.s_addr) != 0) { severe("rozofs_host2ip failed for host : %s, %s", host, strerror(errno)); return 0; } if ((port = pmap_getport(&server, prog, vers, IPPROTO_TCP)) == 0) { //warning("pmap_getport failed %s (%x:%d) %s", host, (unsigned int)prog, (int)vers, clnt_spcreateerror("")); errno = EPROTO; return 0; } info("rpc service %s (%x:%d) available on port %d", host, (unsigned int)prog, (int)vers, port); return port; }
/* Request port mapper to give the port number of a TCP rpc service on a remote host @param host The remote host name @param prog The program number @param vers The version number of the program */ static int get_service_tcp_port(char *host ,unsigned long prog, unsigned long vers) { struct sockaddr_in server; struct hostent *hp; int port = 0; server.sin_family = AF_INET; if ((hp = gethostbyname(host)) == 0) { severe("gethostbyname failed for host : %s, %s", host,strerror(errno)); return 0; } bcopy((char *) hp->h_addr, (char *) &server.sin_addr, hp->h_length); if ((port = pmap_getport(&server, prog, vers, IPPROTO_TCP)) == 0) { //warning("pmap_getport failed %s (%x:%d) %s", host, (unsigned int)prog, (int)vers, clnt_spcreateerror("")); errno = EPROTO; return 0; } info("rpc service %s (%x:%d) available on port %d", host, (unsigned int)prog, (int)vers, port); return port; }
/* * "Connect" to the UCP socket advertised as "prog" on "host" and * return the connected socket. */ int udp_connect(char *host, u_long prog, int rdwr) { struct hostent *h; struct sockaddr_in sin; int sock; #ifndef NO_PORTMAPPER u_short port; #endif if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { perror("socket"); exit(1); } sock_optimize(sock, rdwr); if (!(h = gethostbyname(host))) { perror(host); exit(2); } bzero((void *) &sin, sizeof(sin)); sin.sin_family = AF_INET; bcopy((void*)h->h_addr, (void *) &sin.sin_addr, h->h_length); #ifndef PORTMAP sin.sin_port = htons(prog); #else port = pmap_getport(&sin, prog, (u_long)1, IPPROTO_UDP); if (!port) { perror("lib UDP: No port found"); exit(3); } sin.sin_port = htons(port); #endif if (connect(sock, (struct sockaddr*)&sin, sizeof(sin)) < 0) { perror("connect"); exit(4); } return (sock); }
ushort getrpcport(char *host, rpcprog_t prognum, rpcvers_t versnum, rpcprot_t proto) { struct sockaddr_in address; static int getrpcport_called = 0; if(getrpcport_called == 0) { getrpcport_called = 1; if(rpcTaskInit() == ERROR) { return 0; } } memset( (char *)&address, 0, sizeof(address)); if ((address.sin_addr.s_addr = inet_addr (host)) == ERROR && (address.sin_addr.s_addr = hostGetByName (host)) == ERROR) { return 0; } address.sin_family = AF_INET; address.sin_port = 0; return pmap_getport(&address, (u_long)prognum, (u_long)versnum, IPPROTO_TCP); }
int main(int argc, char *argv[]) { uint32_t port; setvbuf(stdout, NULL, _IONBF, 0); if (argc != 2) { fprintf(stderr, "Usage: %s <hostname>\n", argv[0]); return 1; } if (!rpcbind_set_host(argv[1])) return 1; port = pmap_getport(&rpcbind_addr, PROGNUM, VERSNUM, IPPROTO_UDP); if (port == 0) { fprintf(stderr, "Cannot get registered port for program %d\n", PROGNUM); return 1; } printf("server port is %u\n", port); if (!rpcbind_callit_exploit()) return 1; printf("Sleep for 1 second\n"); sleep(1); if (!rpcbind_ping()) { fprintf(stderr, "Oh no. It seems we killed rpcbind\n"); return 1; } printf("Congratulations. Looks like your rpcbind survived.\n"); return 0; }
/* libc_hidden_proto(clntudp_bufcreate) */ CLIENT * clntudp_bufcreate (struct sockaddr_in *raddr, u_long program, u_long version, struct timeval wait, int *sockp, u_int sendsz, u_int recvsz) { CLIENT *cl; struct cu_data *cu = NULL; struct rpc_msg call_msg; cl = (CLIENT *) mem_alloc (sizeof (CLIENT)); sendsz = ((sendsz + 3) / 4) * 4; recvsz = ((recvsz + 3) / 4) * 4; cu = (struct cu_data *) mem_alloc (sizeof (*cu) + sendsz + recvsz); if (cl == NULL || cu == NULL) { struct rpc_createerr *ce = &get_rpc_createerr (); #ifdef USE_IN_LIBIO if (_IO_fwide (stderr, 0) > 0) (void) fwprintf (stderr, L"%s", _("clntudp_create: out of memory\n")); else #endif (void) fputs (_("clntudp_create: out of memory\n"), stderr); ce->cf_stat = RPC_SYSTEMERROR; ce->cf_error.re_errno = ENOMEM; goto fooy; } cu->cu_outbuf = &cu->cu_inbuf[recvsz]; if (raddr->sin_port == 0) { u_short port; if ((port = pmap_getport (raddr, program, version, IPPROTO_UDP)) == 0) { goto fooy; } raddr->sin_port = htons (port); } cl->cl_ops = &udp_ops; cl->cl_private = (caddr_t) cu; cu->cu_raddr = *raddr; cu->cu_rlen = sizeof (cu->cu_raddr); cu->cu_wait = wait; cu->cu_total.tv_sec = -1; cu->cu_total.tv_usec = -1; cu->cu_sendsz = sendsz; cu->cu_recvsz = recvsz; call_msg.rm_xid = _create_xid (); call_msg.rm_direction = CALL; call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; call_msg.rm_call.cb_prog = program; call_msg.rm_call.cb_vers = version; xdrmem_create (&(cu->cu_outxdrs), cu->cu_outbuf, sendsz, XDR_ENCODE); if (!xdr_callhdr (&(cu->cu_outxdrs), &call_msg)) { goto fooy; } cu->cu_xdrpos = XDR_GETPOS (&(cu->cu_outxdrs)); if (*sockp < 0) { int dontblock = 1; *sockp = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (*sockp < 0) { struct rpc_createerr *ce = &get_rpc_createerr (); ce->cf_stat = RPC_SYSTEMERROR; ce->cf_error.re_errno = errno; goto fooy; } /* attempt to bind to prov port */ (void) bindresvport (*sockp, (struct sockaddr_in *) 0); /* the sockets rpc controls are non-blocking */ (void) ioctl (*sockp, FIONBIO, (char *) &dontblock); #ifdef IP_RECVERR { int on = 1; setsockopt(*sockp, SOL_IP, IP_RECVERR, &on, sizeof(on)); } #endif cu->cu_closeit = TRUE; } else { cu->cu_closeit = FALSE; } cu->cu_sock = *sockp; cl->cl_auth = authnone_create (); return cl; fooy: if (cu) mem_free ((caddr_t) cu, sizeof (*cu) + sendsz + recvsz); if (cl) mem_free ((caddr_t) cl, sizeof (CLIENT)); return (CLIENT *) NULL; }
/** * Init of the load balancing group associated with the exportd (used by rozofsmount only) @param exportclt: data structure that describes the remote exportd @param prog: export program name @param vers: exportd program version @param port_num: tcp port of the exportd (0 for dynamic port ) @param supervision_callback: supervision callback (NULL if none) @retval 0 on success @retval < 0 on error (see errno for details) */ int export_lbg_initialize(exportclt_t *exportclt ,unsigned long prog, unsigned long vers,uint32_t port_num, af_stream_poll_CBK_t supervision_callback) { int status = -1; struct sockaddr_in server; struct hostent *hp; int port = 0; int lbg_size; int export_index=0; char * pHost; int ret; DEBUG_FUNCTION; rpcclt_t * client = &exportclt->rpcclt; server.sin_family = AF_INET; lbg_size = 0; for (export_index=0; export_index < ROZOFS_HOST_LIST_MAX_HOST; export_index++) { pHost = rozofs_host_list_get_host(export_index); if (pHost == NULL) break; if ((hp = gethostbyname(pHost)) == 0) { severe("gethostbyname failed for host : %s, %s", pHost, strerror(errno)); continue; } bcopy((char *) hp->h_addr, (char *) &server.sin_addr, hp->h_length); if (port_num == 0) { if ((port = pmap_getport(&server, prog, vers, IPPROTO_TCP)) == 0) { warning("pmap_getport failed%s", clnt_spcreateerror("")); errno = EPROTO; goto out; } server.sin_port = htons(port); } else { server.sin_port = htons(port_num); } /* ** store the IP address and port in the list of the endpoint */ my_list[lbg_size].remote_port_host = ntohs(server.sin_port); my_list[lbg_size].remote_ipaddr_host = ntohl(server.sin_addr.s_addr); lbg_size++; } if (lbg_size == 0) goto out; af_inet_exportd_conf.recv_srv_type = ROZOFS_RPC_SRV; af_inet_exportd_conf.rpc_recv_max_sz = rozofs_large_tx_recv_size; /* ** allocate a load balancing group */ client->lbg_id = north_lbg_create_no_conf(); if (client->lbg_id < 0) { /* ** cannot create the load balancing group */ severe("Out of lbg context while creating EXPORTD load balancing group"); goto out; } /* ** put the supervision callback if any declared */ #if 1 if (supervision_callback != NULL) { ret = north_lbg_attach_application_supervision_callback(client->lbg_id,supervision_callback); if (ret < 0) { /* ** cannot create the load balancing group */ severe("failure while configuring EXPORTD load balancing group"); goto out; } ret = north_lbg_set_application_tmo4supervision(client->lbg_id,3); if (ret < 0) { /* ** cannot create the load balancing group */ severe("failure while configuring EXPORTD load balancing group"); goto out; } north_lbg_set_active_standby_mode(client->lbg_id); } #endif client->lbg_id = north_lbg_configure_af_inet(client->lbg_id,"METADATA",INADDR_ANY,0,my_list,ROZOFS_SOCK_FAMILY_EXPORT_NORTH, lbg_size,&af_inet_exportd_conf,0); if (client->lbg_id >= 0) { status = 0; /* ** the timer is started only to address the case of a dynamic port */ if (port_num == 0) export_lbg_start_timer (exportclt); return status; } severe("Cannot create Load Balancing Group for Exportd"); out: return status; }
/* * Create a UDP based client handle. * If *sockp<0, *sockp is set to a newly created UPD socket. * If raddr->sin_port is 0 a binder on the remote machine * is consulted for the correct port number. * NB: It is the clients responsibility to close *sockp. * NB: The rpch->cl_auth is initialized to null authentication. * Caller may wish to set this something more useful. * * wait is the amount of time used between retransmitting a call if * no response has been heard; retransmition occurs until the actual * rpc call times out. * * sendsz and recvsz are the maximum allowable packet sizes that can be * sent and received. */ CLIENT *clntudp_bufcreate(struct sockaddr_in *raddr, unsigned long program, unsigned long version, struct timeval wait, int *sockp, unsigned int sendsz, unsigned int recvsz) { CLIENT *cl; register struct cu_data *cu = NULL; struct rpc_msg call_msg; static int xid_count = 0; cl = (CLIENT *) rt_malloc (sizeof(CLIENT)); if (cl == NULL) { rt_kprintf("clntudp_create: out of memory\n"); goto fooy; } sendsz = ((sendsz + 3) / 4) * 4; recvsz = ((recvsz + 3) / 4) * 4; cu = (struct cu_data *) rt_malloc (sizeof(*cu) + sendsz + recvsz); if (cu == NULL) { rt_kprintf("clntudp_create: out of memory\n"); goto fooy; } cu->cu_outbuf = &cu->cu_inbuf[recvsz]; if (raddr->sin_port == 0) { unsigned short port; if ((port = pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) { goto fooy; } raddr->sin_port = htons(port); } cl->cl_ops = &udp_ops; cl->cl_private = (char*) cu; cu->cu_raddr = *raddr; cu->cu_rlen = sizeof(cu->cu_raddr); cu->cu_wait = wait; cu->cu_total.tv_sec = -1; cu->cu_total.tv_usec = -1; cu->cu_sendsz = sendsz; cu->cu_recvsz = recvsz; call_msg.rm_xid = ((unsigned long)rt_thread_self()) ^ ((unsigned long)rt_tick_get()) ^ (xid_count++); call_msg.rm_direction = CALL; call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; call_msg.rm_call.cb_prog = program; call_msg.rm_call.cb_vers = version; xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, sendsz, XDR_ENCODE); if (!xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) { goto fooy; } cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs)); if (*sockp < 0) { int dontblock = 1; *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (*sockp < 0) { rt_kprintf("create socket error\n"); goto fooy; } cu->cu_closeit = TRUE; } else { cu->cu_closeit = FALSE; } cu->cu_sock = *sockp; cl->cl_auth = authnone_create(); return (cl); fooy: if (cu) rt_free(cu); if (cl) rt_free(cl); return ((CLIENT *) NULL); }
/* * Create a UDP based client handle. * If *sockp<0, *sockp is set to a newly created UPD socket. * If raddr->sin_port is 0 a binder on the remote machine * is consulted for the correct port number. * NB: It is the clients responsibility to close *sockp. * NB: The rpch->cl_auth is initialized to null authentication. * Caller may wish to set this something more useful. * * wait is the amount of time used between retransmitting a call if * no response has been heard; retransmission occurs until the actual * rpc call times out. * * sendsz and recvsz are the maximum allowable packet sizes that can be * sent and received. */ CLIENT * __libc_clntudp_bufcreate (struct sockaddr_in *raddr, u_long program, u_long version, struct timeval wait, int *sockp, u_int sendsz, u_int recvsz, int flags) { CLIENT *cl; struct cu_data *cu = NULL; struct rpc_msg call_msg; cl = (CLIENT *) mem_alloc (sizeof (CLIENT)); sendsz = ((sendsz + 3) / 4) * 4; recvsz = ((recvsz + 3) / 4) * 4; cu = (struct cu_data *) mem_alloc (sizeof (*cu) + sendsz + recvsz); if (cl == NULL || cu == NULL) { struct rpc_createerr *ce = &get_rpc_createerr (); (void) __fxprintf (NULL, "%s: %s", "clntudp_create", _("out of memory\n")); ce->cf_stat = RPC_SYSTEMERROR; ce->cf_error.re_errno = ENOMEM; goto fooy; } cu->cu_outbuf = &cu->cu_inbuf[recvsz]; if (raddr->sin_port == 0) { u_short port; if ((port = pmap_getport (raddr, program, version, IPPROTO_UDP)) == 0) { goto fooy; } raddr->sin_port = htons (port); } cl->cl_ops = (struct clnt_ops *) &udp_ops; cl->cl_private = (caddr_t) cu; cu->cu_raddr = *raddr; cu->cu_rlen = sizeof (cu->cu_raddr); cu->cu_wait = wait; cu->cu_total.tv_sec = -1; cu->cu_total.tv_usec = -1; cu->cu_sendsz = sendsz; cu->cu_recvsz = recvsz; call_msg.rm_xid = _create_xid (); call_msg.rm_direction = CALL; call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; call_msg.rm_call.cb_prog = program; call_msg.rm_call.cb_vers = version; xdrmem_create (&(cu->cu_outxdrs), cu->cu_outbuf, sendsz, XDR_ENCODE); if (!xdr_callhdr (&(cu->cu_outxdrs), &call_msg)) { goto fooy; } cu->cu_xdrpos = XDR_GETPOS (&(cu->cu_outxdrs)); if (*sockp < 0) { #ifdef SOCK_NONBLOCK # ifndef __ASSUME_SOCK_CLOEXEC if (__have_sock_cloexec >= 0) # endif { *sockp = __socket (AF_INET, SOCK_DGRAM|SOCK_NONBLOCK|flags, IPPROTO_UDP); # ifndef __ASSUME_SOCK_CLOEXEC if (__have_sock_cloexec == 0) __have_sock_cloexec = *sockp >= 0 || errno != EINVAL ? 1 : -1; # endif } #endif #ifndef __ASSUME_SOCK_CLOEXEC # ifdef SOCK_CLOEXEC if (__have_sock_cloexec < 0) # endif { *sockp = __socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); # ifdef SOCK_CLOEXEC if (flags & SOCK_CLOEXEC) __fcntl (*sockp, F_SETFD, FD_CLOEXEC); # endif } #endif if (__builtin_expect (*sockp < 0, 0)) { struct rpc_createerr *ce = &get_rpc_createerr (); ce->cf_stat = RPC_SYSTEMERROR; ce->cf_error.re_errno = errno; goto fooy; } /* attempt to bind to prov port */ (void) bindresvport (*sockp, (struct sockaddr_in *) 0); #ifndef __ASSUME_SOCK_CLOEXEC # ifdef SOCK_CLOEXEC if (__have_sock_cloexec < 0) # endif { /* the sockets rpc controls are non-blocking */ int dontblock = 1; (void) __ioctl (*sockp, FIONBIO, (char *) &dontblock); } #endif #ifdef IP_RECVERR { int on = 1; __setsockopt (*sockp, SOL_IP, IP_RECVERR, &on, sizeof(on)); } #endif cu->cu_closeit = TRUE; } else { cu->cu_closeit = FALSE; } cu->cu_sock = *sockp; cl->cl_auth = authnone_create (); return cl; fooy: if (cu) mem_free ((caddr_t) cu, sizeof (*cu) + sendsz + recvsz); if (cl) mem_free ((caddr_t) cl, sizeof (CLIENT)); return (CLIENT *) NULL; }
/* * A common clnt create routine */ static CLIENT * clnt_com_create(struct sockaddr_in *raddr, rpcprog_t prog, rpcvers_t vers, int *sockp, uint_t sendsz, uint_t recvsz, char *tp) { CLIENT *cl; int madefd = FALSE; int fd = *sockp; struct t_info tinfo; struct netconfig *nconf; int port; struct netbuf bindaddr; bool_t locked = TRUE; (void) mutex_lock(&rpcsoc_lock); if ((nconf = __rpc_getconfip(tp)) == NULL) { rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; (void) mutex_unlock(&rpcsoc_lock); return (NULL); } if (fd == RPC_ANYSOCK) { fd = t_open(nconf->nc_device, O_RDWR, &tinfo); if (fd == -1) goto syserror; RPC_RAISEFD(fd); madefd = TRUE; } else { if (t_getinfo(fd, &tinfo) == -1) goto syserror; } if (raddr->sin_port == 0) { uint_t proto; ushort_t sport; /* pmap_getport is recursive */ (void) mutex_unlock(&rpcsoc_lock); proto = strcmp(tp, "udp") == 0 ? IPPROTO_UDP : IPPROTO_TCP; sport = pmap_getport(raddr, prog, vers, proto); if (sport == 0) { locked = FALSE; goto err; } raddr->sin_port = htons(sport); /* pmap_getport is recursive */ (void) mutex_lock(&rpcsoc_lock); } /* Transform sockaddr_in to netbuf */ bindaddr.maxlen = bindaddr.len = __rpc_get_a_size(tinfo.addr); bindaddr.buf = (char *)raddr; (void) __rpc_bindresvport(fd, NULL, &port, 0); cl = clnt_tli_create(fd, nconf, &bindaddr, prog, vers, sendsz, recvsz); if (cl) { if (madefd == TRUE) { /* * The fd should be closed while destroying the handle. */ (void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL); *sockp = fd; } (void) freenetconfigent(nconf); (void) mutex_unlock(&rpcsoc_lock); return (cl); } goto err; syserror: rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; rpc_createerr.cf_error.re_terrno = t_errno; err: if (madefd == TRUE) (void) t_close(fd); (void) freenetconfigent(nconf); if (locked == TRUE) (void) mutex_unlock(&rpcsoc_lock); return (NULL); }
int getnfsargs(char *spec, struct nfs_args *nfsargsp) { CLIENT *clp; struct hostent *hp; static struct sockaddr_in saddr; struct timeval pertry, try; enum clnt_stat clnt_stat; int so = RPC_ANYSOCK, i, nfsvers, mntvers, orgcnt; char *hostp, *delimp; u_short tport; static struct nfhret nfhret; static char nam[MNAMELEN + 1]; if (strlcpy(nam, spec, sizeof(nam)) >= sizeof(nam)) { errx(1, "hostname too long"); } if ((delimp = strchr(spec, '@')) != NULL) { hostp = delimp + 1; } else if ((delimp = strchr(spec, ':')) != NULL) { hostp = spec; spec = delimp + 1; } else { warnx("no <host>:<dirpath> or <dirpath>@<host> spec"); return (0); } *delimp = '\0'; /* * Handle an internet host address */ if (inet_aton(hostp, &saddr.sin_addr) == 0) { hp = gethostbyname(hostp); if (hp == NULL) { warnx("can't resolve address for host %s", hostp); return (0); } memcpy(&saddr.sin_addr, hp->h_addr, hp->h_length); } if (force2) { nfsvers = NFS_VER2; mntvers = RPCMNT_VER1; } else { nfsvers = NFS_VER3; mntvers = RPCMNT_VER3; } orgcnt = retrycnt; tryagain: nfhret.stat = EACCES; /* Mark not yet successful */ while (retrycnt > 0) { saddr.sin_family = AF_INET; saddr.sin_port = htons(PMAPPORT); if ((tport = port_no ? port_no : pmap_getport(&saddr, RPCPROG_NFS, nfsvers, nfsargsp->sotype == SOCK_STREAM ? IPPROTO_TCP : IPPROTO_UDP)) == 0) { if ((opflags & ISBGRND) == 0) clnt_pcreateerror("NFS Portmap"); } else { saddr.sin_port = 0; pertry.tv_sec = 10; pertry.tv_usec = 0; if (mnttcp_ok && nfsargsp->sotype == SOCK_STREAM) clp = clnttcp_create(&saddr, RPCPROG_MNT, mntvers, &so, 0, 0); else clp = clntudp_create(&saddr, RPCPROG_MNT, mntvers, pertry, &so); if (clp == NULL) { if ((opflags & ISBGRND) == 0) clnt_pcreateerror("Cannot MNT RPC"); } else { clp->cl_auth = authunix_create_default(); try.tv_sec = 10; try.tv_usec = 0; nfhret.auth = RPCAUTH_UNIX; nfhret.vers = mntvers; clnt_stat = clnt_call(clp, RPCMNT_MOUNT, xdr_dir, spec, xdr_fh, &nfhret, try); if (clnt_stat != RPC_SUCCESS) { if (clnt_stat == RPC_PROGVERSMISMATCH) { if (nfsvers == NFS_VER3 && !force3) { retrycnt = orgcnt; nfsvers = NFS_VER2; mntvers = RPCMNT_VER1; nfsargsp->flags &= ~NFSMNT_NFSV3; goto tryagain; } else { warnx("%s", clnt_sperror(clp, "MNT RPC")); } } if ((opflags & ISBGRND) == 0) warnx("%s", clnt_sperror(clp, "bad MNT RPC")); } else { auth_destroy(clp->cl_auth); clnt_destroy(clp); retrycnt = 0; } } } if (--retrycnt > 0) { if (opflags & BGRND) { opflags &= ~BGRND; if ((i = fork())) { if (i == -1) err(1, "fork"); exit(0); } (void) setsid(); (void) close(STDIN_FILENO); (void) close(STDOUT_FILENO); (void) close(STDERR_FILENO); (void) chdir("/"); opflags |= ISBGRND; } sleep(60); } } if (nfhret.stat) { if (opflags & ISBGRND) exit(1); errno = nfhret.stat; warnx("can't access %s: %s", spec, strerror(nfhret.stat)); return (0); } saddr.sin_port = htons(tport); nfsargsp->addr = (struct sockaddr *) &saddr; nfsargsp->addrlen = sizeof (saddr); nfsargsp->fh = nfhret.nfh; nfsargsp->fhsize = nfhret.fhsize; nfsargsp->hostname = nam; return (1); }
/* * A common clnt create routine */ static CLIENT * clnt_com_create(struct sockaddr_in *raddr, rpcprog_t prog, rpcvers_t vers, int *sockp, u_int sendsz, u_int recvsz, const char *tp) { CLIENT *cl; int madefd = FALSE; int fd; struct netconfig *nconf; struct netbuf bindaddr; _DIAGASSERT(raddr != NULL); _DIAGASSERT(sockp != NULL); _DIAGASSERT(tp != NULL); fd = *sockp; mutex_lock(&rpcsoc_lock); if ((nconf = __rpc_getconfip(tp)) == NULL) { rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; mutex_unlock(&rpcsoc_lock); return (NULL); } if (fd == RPC_ANYSOCK) { fd = __rpc_nconf2fd(nconf); if (fd == -1) goto syserror; madefd = TRUE; } if (raddr->sin_port == 0) { u_int proto; u_short sport; mutex_unlock(&rpcsoc_lock); /* pmap_getport is recursive */ proto = strcmp(tp, "udp") == 0 ? IPPROTO_UDP : IPPROTO_TCP; sport = pmap_getport(raddr, (u_long)prog, (u_long)vers, proto); if (sport == 0) { goto err; } raddr->sin_port = htons(sport); mutex_lock(&rpcsoc_lock); /* pmap_getport is recursive */ } /* Transform sockaddr_in to netbuf */ bindaddr.maxlen = bindaddr.len = sizeof (struct sockaddr_in); bindaddr.buf = raddr; (void)bindresvport(fd, NULL); cl = clnt_tli_create(fd, nconf, &bindaddr, prog, vers, sendsz, recvsz); if (cl) { if (madefd == TRUE) { /* * The fd should be closed while destroying the handle. */ (void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL); *sockp = fd; } (void) freenetconfigent(nconf); mutex_unlock(&rpcsoc_lock); return (cl); } goto err; syserror: rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; err: if (madefd == TRUE) (void) close(fd); (void) freenetconfigent(nconf); mutex_unlock(&rpcsoc_lock); return (NULL); }
/* * Create a client handle for a tcp/ip connection. * If *sockp<0, *sockp is set to a newly created TCP socket and it is * connected to raddr. If *sockp non-negative then * raddr is ignored. The rpc/tcp package does buffering * similar to stdio, so the client must pick send and receive buffer sizes,]; * 0 => use the default. * If raddr->sin_port is 0, then a binder on the remote machine is * consulted for the right port number. * NB: *sockp is copied into a private area. * NB: It is the client's responsibility to close *sockp, unless * clnttcp_create() was called with *sockp = -1 (so it created * the socket), and CLNT_DESTROY() is used. * NB: The rpch->cl_auth is set null authentication. Caller may wish to set this * something more useful. */ CLIENT * clnttcp_create(struct sockaddr_in *raddr, u_long prog, u_long vers, int *sockp, u_int sendsz, u_int recvsz) { CLIENT *h; struct ct_data *ct = NULL; struct timeval now; struct rpc_msg call_msg; h = (CLIENT *)mem_alloc(sizeof(*h)); if (h == NULL) { rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; goto fooy; } ct = (struct ct_data *)mem_alloc(sizeof(*ct)); if (ct == NULL) { rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; goto fooy; } /* * If no port number given ask the pmap for one */ if (raddr->sin_port == 0) { u_short port; if ((port = pmap_getport(raddr, prog, vers, IPPROTO_TCP)) == 0) { mem_free((caddr_t)ct, sizeof(struct ct_data)); mem_free((caddr_t)h, sizeof(CLIENT)); return (NULL); } raddr->sin_port = htons(port); } /* * If no socket given, open one */ if (*sockp < 0) { *sockp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); (void)bindresvport(*sockp, NULL); if ((*sockp < 0) || (connect(*sockp, (struct sockaddr *)raddr, sizeof(*raddr)) < 0)) { rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; if (*sockp != -1) (void)close(*sockp); goto fooy; } ct->ct_closeit = TRUE; } else { ct->ct_closeit = FALSE; } /* * Set up private data struct */ ct->ct_sock = *sockp; ct->ct_wait.tv_usec = 0; ct->ct_waitset = FALSE; ct->ct_addr = *raddr; /* * Initialize call message */ (void)gettimeofday(&now, NULL); call_msg.rm_xid = arc4random(); call_msg.rm_direction = CALL; call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; call_msg.rm_call.cb_prog = prog; call_msg.rm_call.cb_vers = vers; /* * pre-serialize the static part of the call msg and stash it away */ xdrmem_create(&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE, XDR_ENCODE); if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) { if (ct->ct_closeit) { (void)close(*sockp); } goto fooy; } ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs)); XDR_DESTROY(&(ct->ct_xdrs)); /* * Create a client handle which uses xdrrec for serialization * and authnone for authentication. */ xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz, (caddr_t)ct, (int(*)(caddr_t, caddr_t, int))readtcp, (int(*)(caddr_t, caddr_t, int))writetcp); h->cl_ops = &tcp_ops; h->cl_private = (caddr_t) ct; h->cl_auth = authnone_create(); if (h->cl_auth == NULL) { rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; goto fooy; } return (h); fooy: /* * Something goofed, free stuff and barf */ if (ct) mem_free((caddr_t)ct, sizeof(struct ct_data)); if (h) mem_free((caddr_t)h, sizeof(CLIENT)); return (NULL); }
/* * Create a UDP based client handle. * If *sockp<0, *sockp is set to a newly created UPD socket. * If raddr->sin_port is 0 a binder on the remote machine * is consulted for the correct port number. * NB: It is the clients responsibility to close *sockp. * NB: The rpch->cl_auth is initialized to null authentication. * Caller may wish to set this something more useful. * * wait is the amount of time used between retransmitting a call if * no response has been heard; retransmition occurs until the actual * rpc call times out. * * sendsz and recvsz are the maximum allowable packet sizes that can be * sent and received. */ CLIENT * clntudp_bufcreate( struct sockaddr_in *raddr, unsigned long program, unsigned long version, struct timeval waitval, register int *sockp, unsigned sendsz, unsigned recvsz) { CLIENT *cl; register struct cu_data *cu = NULL; struct timeval now; struct rpc_msg call_msg; cl = (CLIENT *)mem_alloc(sizeof(CLIENT)); if (cl == NULL) { (void) fprintf(stderr, "clntudp_create: out of memory\n"); rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; goto fooy; } sendsz = ((sendsz + 3) / 4) * 4; recvsz = ((recvsz + 3) / 4) * 4; cu = (struct cu_data *)mem_alloc( (size_t)(sizeof(*cu) + sendsz + recvsz)); if (cu == NULL) { (void) fprintf(stderr, "clntudp_create: out of memory\n"); rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; goto fooy; } cu->cu_inbuf = (char*)&cu->cu_dummy; /* uint32_t aligned */ cu->cu_outbuf = &cu->cu_inbuf[recvsz]; (void)gettimeofday(&now, NULL); if (raddr->sin_port == 0) { unsigned short port; if ((port = pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) { goto fooy; } raddr->sin_port = htons(port); } cl->cl_ops = &udp_ops; cl->cl_private = (char*)cu; cu->cu_raddr = *raddr; cu->cu_rlen = sizeof (cu->cu_raddr); cu->cu_wait = waitval; cu->cu_total.tv_sec = -1; cu->cu_total.tv_usec = -1; cu->cu_sendsz = sendsz; cu->cu_recvsz = recvsz; call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec; call_msg.rm_direction = CALL; call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; call_msg.rm_call.cb_prog = program; call_msg.rm_call.cb_vers = version; xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, sendsz, XDR_ENCODE); if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) { goto fooy; } cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs)); if (*sockp < 0) { int flags; struct sockaddr_in myaddr; int sock; sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) { rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; goto fooy; } (void)memset(&myaddr, 0, sizeof(myaddr)); myaddr.sin_family = AF_INET; myaddr.sin_addr.s_addr = htonl(INADDR_ANY); myaddr.sin_port = htons(0); if (bind(sock, (struct sockaddr*)&myaddr, sizeof(myaddr)) == -1) { rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; (void)close(sock); goto fooy; } else { *sockp = sock; } /* the sockets rpc controls are non-blocking */ flags = fcntl(*sockp, F_GETFL); (void)fcntl(*sockp, F_SETFL, flags | O_NONBLOCK); cu->cu_closeit = TRUE; } else { cu->cu_closeit = FALSE; } cu->cu_sock = *sockp; cl->cl_auth = authnone_create(); return (cl); fooy: if (cu) mem_free((char*)cu, sizeof(*cu) + sendsz + recvsz); if (cl) mem_free((char*)cl, sizeof(CLIENT)); return ((CLIENT *)NULL); }
// NB: mp->xxx fields may be trashed on exit static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts) { CLIENT *mclient; char *hostname; char *pathname; char *mounthost; struct nfs_mount_data data; char *opt; struct hostent *hp; struct sockaddr_in server_addr; struct sockaddr_in mount_server_addr; int msock, fsock; union { struct fhstatus nfsv2; struct mountres3 nfsv3; } status; int daemonized; char *s; int port; int mountport; int proto; int bg; int soft; int intr; int posix; int nocto; int noac; int nolock; int retry; int tcp; int mountprog; int mountvers; int nfsprog; int nfsvers; int retval; find_kernel_nfs_mount_version(); daemonized = 0; mounthost = NULL; retval = ETIMEDOUT; msock = fsock = -1; mclient = NULL; /* NB: hostname, mounthost, filteropts must be free()d prior to return */ filteropts = xstrdup(filteropts); /* going to trash it later... */ hostname = xstrdup(mp->mnt_fsname); /* mount_main() guarantees that ':' is there */ s = strchr(hostname, ':'); pathname = s + 1; *s = '\0'; /* Ignore all but first hostname in replicated mounts until they can be fully supported. ([email protected]) */ s = strchr(hostname, ','); if (s) { *s = '\0'; bb_error_msg("warning: multiple hostnames not supported"); } server_addr.sin_family = AF_INET; if (!inet_aton(hostname, &server_addr.sin_addr)) { hp = gethostbyname(hostname); if (hp == NULL) { bb_herror_msg("%s", hostname); goto fail; } if (hp->h_length > sizeof(struct in_addr)) { bb_error_msg("got bad hp->h_length"); hp->h_length = sizeof(struct in_addr); } memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length); } memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr)); /* add IP address to mtab options for use when unmounting */ if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */ mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr)); } else { char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts, mp->mnt_opts[0] ? "," : "", inet_ntoa(server_addr.sin_addr)); free(mp->mnt_opts); mp->mnt_opts = tmp; } /* Set default options. * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to * let the kernel decide. * timeo is filled in after we know whether it'll be TCP or UDP. */ memset(&data, 0, sizeof(data)); data.retrans = 3; data.acregmin = 3; data.acregmax = 60; data.acdirmin = 30; data.acdirmax = 60; data.namlen = NAME_MAX; bg = 0; soft = 0; intr = 0; posix = 0; nocto = 0; nolock = 0; noac = 0; retry = 10000; /* 10000 minutes ~ 1 week */ tcp = 0; mountprog = MOUNTPROG; mountvers = 0; port = 0; mountport = 0; nfsprog = 100003; nfsvers = 0; /* parse options */ if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) { char *opteq = strchr(opt, '='); if (opteq) { static const char *const options[] = { /* 0 */ "rsize", /* 1 */ "wsize", /* 2 */ "timeo", /* 3 */ "retrans", /* 4 */ "acregmin", /* 5 */ "acregmax", /* 6 */ "acdirmin", /* 7 */ "acdirmax", /* 8 */ "actimeo", /* 9 */ "retry", /* 10 */ "port", /* 11 */ "mountport", /* 12 */ "mounthost", /* 13 */ "mountprog", /* 14 */ "mountvers", /* 15 */ "nfsprog", /* 16 */ "nfsvers", /* 17 */ "vers", /* 18 */ "proto", /* 19 */ "namlen", /* 20 */ "addr", NULL }; int val = xatoi_u(opteq + 1); *opteq = '\0'; switch (index_in_str_array(options, opt)) { case 0: // "rsize" data.rsize = val; break; case 1: // "wsize" data.wsize = val; break; case 2: // "timeo" data.timeo = val; break; case 3: // "retrans" data.retrans = val; break; case 4: // "acregmin" data.acregmin = val; break; case 5: // "acregmax" data.acregmax = val; break; case 6: // "acdirmin" data.acdirmin = val; break; case 7: // "acdirmax" data.acdirmax = val; break; case 8: // "actimeo" data.acregmin = val; data.acregmax = val; data.acdirmin = val; data.acdirmax = val; break; case 9: // "retry" retry = val; break; case 10: // "port" port = val; break; case 11: // "mountport" mountport = val; break; case 12: // "mounthost" mounthost = xstrndup(opteq+1, strcspn(opteq+1," \t\n\r,")); break; case 13: // "mountprog" mountprog = val; break; case 14: // "mountvers" mountvers = val; break; case 15: // "nfsprog" nfsprog = val; break; case 16: // "nfsvers" case 17: // "vers" nfsvers = val; break; case 18: // "proto" if (!strncmp(opteq+1, "tcp", 3)) tcp = 1; else if (!strncmp(opteq+1, "udp", 3)) tcp = 0; else bb_error_msg("warning: unrecognized proto= option"); break; case 19: // "namlen" if (nfs_mount_version >= 2) data.namlen = val; else bb_error_msg("warning: option namlen is not supported\n"); break; case 20: // "addr" - ignore break; default: bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val); goto fail; } } else { static const char *const options[] = { "bg", "fg", "soft", "hard", "intr", "posix", "cto", "ac", "tcp", "udp", "lock", NULL }; int val = 1; if (!strncmp(opt, "no", 2)) { val = 0; opt += 2; } switch (index_in_str_array(options, opt)) { case 0: // "bg" bg = val; break; case 1: // "fg" bg = !val; break; case 2: // "soft" soft = val; break; case 3: // "hard" soft = !val; break; case 4: // "intr" intr = val; break; case 5: // "posix" posix = val; break; case 6: // "cto" nocto = !val; break; case 7: // "ac" noac = !val; break; case 8: // "tcp" tcp = val; break; case 9: // "udp" tcp = !val; break; case 10: // "lock" if (nfs_mount_version >= 3) nolock = !val; else bb_error_msg("warning: option nolock is not supported"); break; default: bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt); goto fail; } } } proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP; data.flags = (soft ? NFS_MOUNT_SOFT : 0) | (intr ? NFS_MOUNT_INTR : 0) | (posix ? NFS_MOUNT_POSIX : 0) | (nocto ? NFS_MOUNT_NOCTO : 0) | (noac ? NFS_MOUNT_NOAC : 0); if (nfs_mount_version >= 2) data.flags |= (tcp ? NFS_MOUNT_TCP : 0); if (nfs_mount_version >= 3) data.flags |= (nolock ? NFS_MOUNT_NONLM : 0); if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) { bb_error_msg("NFSv%d not supported", nfsvers); goto fail; } if (nfsvers && !mountvers) mountvers = (nfsvers < 3) ? 1 : nfsvers; if (nfsvers && nfsvers < mountvers) { mountvers = nfsvers; } /* Adjust options if none specified */ if (!data.timeo) data.timeo = tcp ? 70 : 7; data.version = nfs_mount_version; if (vfsflags & MS_REMOUNT) goto do_mount; /* * If the previous mount operation on the same host was * backgrounded, and the "bg" for this mount is also set, * give up immediately, to avoid the initial timeout. */ if (bg && we_saw_this_host_before(hostname)) { daemonized = daemonize(); /* parent or error */ if (daemonized <= 0) { /* parent or error */ retval = -daemonized; goto ret; } } /* create mount daemon client */ /* See if the nfs host = mount host. */ if (mounthost) { if (mounthost[0] >= '0' && mounthost[0] <= '9') { mount_server_addr.sin_family = AF_INET; mount_server_addr.sin_addr.s_addr = inet_addr(hostname); } else { hp = gethostbyname(mounthost); if (hp == NULL) { bb_herror_msg("%s", mounthost); goto fail; } else { if (hp->h_length > sizeof(struct in_addr)) { bb_error_msg("got bad hp->h_length?"); hp->h_length = sizeof(struct in_addr); } mount_server_addr.sin_family = AF_INET; memcpy(&mount_server_addr.sin_addr, hp->h_addr, hp->h_length); } } } /* * The following loop implements the mount retries. When the mount * times out, and the "bg" option is set, we background ourself * and continue trying. * * The case where the mount point is not present and the "bg" * option is set, is treated as a timeout. This is done to * support nested mounts. * * The "retry" count specified by the user is the number of * minutes to retry before giving up. */ { struct timeval total_timeout; struct timeval retry_timeout; struct pmap* pm_mnt; time_t t; time_t prevt; time_t timeout; retry_timeout.tv_sec = 3; retry_timeout.tv_usec = 0; total_timeout.tv_sec = 20; total_timeout.tv_usec = 0; timeout = time(NULL) + 60 * retry; prevt = 0; t = 30; retry: /* be careful not to use too many CPU cycles */ if (t - prevt < 30) sleep(30); pm_mnt = get_mountport(&mount_server_addr, mountprog, mountvers, proto, mountport); nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers; /* contact the mount daemon via TCP */ mount_server_addr.sin_port = htons(pm_mnt->pm_port); msock = RPC_ANYSOCK; switch (pm_mnt->pm_prot) { case IPPROTO_UDP: mclient = clntudp_create(&mount_server_addr, pm_mnt->pm_prog, pm_mnt->pm_vers, retry_timeout, &msock); if (mclient) break; mount_server_addr.sin_port = htons(pm_mnt->pm_port); msock = RPC_ANYSOCK; case IPPROTO_TCP: mclient = clnttcp_create(&mount_server_addr, pm_mnt->pm_prog, pm_mnt->pm_vers, &msock, 0, 0); break; default: mclient = 0; } if (!mclient) { if (!daemonized && prevt == 0) error_msg_rpc(clnt_spcreateerror(" ")); } else { enum clnt_stat clnt_stat; /* try to mount hostname:pathname */ mclient->cl_auth = authunix_create_default(); /* make pointers in xdr_mountres3 NULL so * that xdr_array allocates memory for us */ memset(&status, 0, sizeof(status)); if (pm_mnt->pm_vers == 3) clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT, (xdrproc_t) xdr_dirpath, (caddr_t) &pathname, (xdrproc_t) xdr_mountres3, (caddr_t) &status, total_timeout); else clnt_stat = clnt_call(mclient, MOUNTPROC_MNT, (xdrproc_t) xdr_dirpath, (caddr_t) &pathname, (xdrproc_t) xdr_fhstatus, (caddr_t) &status, total_timeout); if (clnt_stat == RPC_SUCCESS) goto prepare_kernel_data; /* we're done */ if (errno != ECONNREFUSED) { error_msg_rpc(clnt_sperror(mclient, " ")); goto fail; /* don't retry */ } /* Connection refused */ if (!daemonized && prevt == 0) /* print just once */ error_msg_rpc(clnt_sperror(mclient, " ")); auth_destroy(mclient->cl_auth); clnt_destroy(mclient); mclient = 0; close(msock); } /* Timeout. We are going to retry... maybe */ if (!bg) goto fail; if (!daemonized) { daemonized = daemonize(); if (daemonized <= 0) { /* parent or error */ retval = -daemonized; goto ret; } } prevt = t; t = time(NULL); if (t >= timeout) /* TODO error message */ goto fail; goto retry; } prepare_kernel_data: if (nfsvers == 2) { if (status.nfsv2.fhs_status != 0) { bb_error_msg("%s:%s failed, reason given by server: %s", hostname, pathname, nfs_strerror(status.nfsv2.fhs_status)); goto fail; } memcpy(data.root.data, (char *) status.nfsv2.fhstatus_u.fhs_fhandle, NFS_FHSIZE); data.root.size = NFS_FHSIZE; memcpy(data.old_root.data, (char *) status.nfsv2.fhstatus_u.fhs_fhandle, NFS_FHSIZE); } else { fhandle3 *my_fhandle; if (status.nfsv3.fhs_status != 0) { bb_error_msg("%s:%s failed, reason given by server: %s", hostname, pathname, nfs_strerror(status.nfsv3.fhs_status)); goto fail; } my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle; memset(data.old_root.data, 0, NFS_FHSIZE); memset(&data.root, 0, sizeof(data.root)); data.root.size = my_fhandle->fhandle3_len; memcpy(data.root.data, (char *) my_fhandle->fhandle3_val, my_fhandle->fhandle3_len); data.flags |= NFS_MOUNT_VER3; } /* create nfs socket for kernel */ if (tcp) { if (nfs_mount_version < 3) { bb_error_msg("NFS over TCP is not supported"); goto fail; } fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); } else fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (fsock < 0) { bb_perror_msg("nfs socket"); goto fail; } if (bindresvport(fsock, 0) < 0) { bb_perror_msg("nfs bindresvport"); goto fail; } if (port == 0) { server_addr.sin_port = PMAPPORT; port = pmap_getport(&server_addr, nfsprog, nfsvers, tcp ? IPPROTO_TCP : IPPROTO_UDP); if (port == 0) port = NFS_PORT; } server_addr.sin_port = htons(port); /* prepare data structure for kernel */ data.fd = fsock; memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr)); strncpy(data.hostname, hostname, sizeof(data.hostname)); /* clean up */ auth_destroy(mclient->cl_auth); clnt_destroy(mclient); close(msock); if (bg) { /* We must wait until mount directory is available */ struct stat statbuf; int delay = 1; while (stat(mp->mnt_dir, &statbuf) == -1) { if (!daemonized) { daemonized = daemonize(); if (daemonized <= 0) { /* parent or error */ retval = -daemonized; goto ret; } } sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */ delay *= 2; if (delay > 30) delay = 30; } } do_mount: /* perform actual mount */ mp->mnt_type = (char*)"nfs"; retval = mount_it_now(mp, vfsflags, (char*)&data); goto ret; fail: /* abort */ if (msock != -1) { if (mclient) { auth_destroy(mclient->cl_auth); clnt_destroy(mclient); } close(msock); } if (fsock != -1) close(fsock); ret: free(hostname); free(mounthost); free(filteropts); return retval; }
int rpcclt_initialize(rpcclt_t * client, const char *host, unsigned long prog, unsigned long vers, unsigned int sendsz, unsigned int recvsz, uint32_t port_num, struct timeval timeout) { int status = -1; struct sockaddr_in server; int one = 1; int port = 0; DEBUG_FUNCTION; if (client->sock >= 0) { /* ** socket reference should be set to -1 at initialization or when released ** if rpcclt_initialize is called with socket not set to -1 we may be loosing ** a socket */ warning("rpcclt_initialize - dest %s:%d - prg 0x%8.8x - socket %d may be lost", host,port_num,(unsigned int)prog,client->sock); } client->client = 0; client->sock = -1; server.sin_family = AF_INET; if (rozofs_host2ip_netw((char*)host, &server.sin_addr.s_addr) != 0) { severe("rozofs_host2ip failed for host : %s, %s", host, strerror(errno)); goto out; } if (port_num == 0) { if ((port = pmap_getport(&server, prog, vers, IPPROTO_TCP)) == 0) { //warning("pmap_getport failed%s", clnt_spcreateerror("")); errno = EPROTO; goto out; } server.sin_port = htons(port); } else { server.sin_port = htons(port_num); } if ((client->sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { goto out; } // Allows other sockets to bind() to this port, unless there is an active // listening socket bound to the port already. if (setsockopt (client->sock, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof (int)) < 0) { goto out; } // Set a timeout value for output operations if (setsockopt (client->sock, SOL_SOCKET, SO_SNDTIMEO, (char *) &timeout, sizeof (timeout)) < 0) { goto out; } // This option specifies what should happen when the socket // of a type that promises reliable delivery still has untransmitted // messages when it is closed struct linger linger; linger.l_onoff = 1; //0 = off (l_linger ignored), nonzero = on linger.l_linger = 0; //0 = discard data, nonzero = wait for data sent if (setsockopt (client->sock, SOL_SOCKET, SO_LINGER, &linger, sizeof (linger)) < 0) { goto out; } // If set, disable the Nagle algorithm. This means that segments are always // sent as soon as possible, even if there is only a small amount of data. if (setsockopt (client->sock, SOL_TCP, TCP_NODELAY, (char *) &one, sizeof (int)) < 0) { goto out; } if ((client->sock < 0) || (connect(client->sock, (struct sockaddr *) &server, sizeof (server)) < 0)) { status = -1; goto out; } if ((client->client = clnttcp_create(&server, prog, vers, &client->sock, sendsz, recvsz)) == NULL) { errno = EPROTO; goto out; } // Set TIMEOUT for this connection clnt_control(client->client, CLSET_TIMEOUT, (char *) &timeout); status = 0; out: if (status != 0) rpcclt_release(client); return status; }
CLIENT * clnttcp_create (struct sockaddr_in *raddr, u_long prog, u_long vers, int *sockp, u_int sendsz, u_int recvsz) { CLIENT *h; struct ct_data *ct; struct rpc_msg call_msg; h = (CLIENT *) mem_alloc (sizeof (*h)); ct = (struct ct_data *) mem_alloc (sizeof (*ct)); if (h == NULL || ct == NULL) { struct rpc_createerr *ce = &get_rpc_createerr (); #ifdef USE_IN_LIBIO if (_IO_fwide (stderr, 0) > 0) (void) fwprintf (stderr, L"%s", _("clnttcp_create: out of memory\n")); else #endif (void) fputs (_("clnttcp_create: out of memory\n"), stderr); ce->cf_stat = RPC_SYSTEMERROR; ce->cf_error.re_errno = ENOMEM; goto fooy; } /* * If no port number given ask the pmap for one */ if (raddr->sin_port == 0) { u_short port; if ((port = pmap_getport (raddr, prog, vers, IPPROTO_TCP)) == 0) { mem_free ((caddr_t) ct, sizeof (struct ct_data)); mem_free ((caddr_t) h, sizeof (CLIENT)); return ((CLIENT *) NULL); } raddr->sin_port = htons (port); } /* * If no socket given, open one */ if (*sockp < 0) { *sockp = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); (void) bindresvport (*sockp, (struct sockaddr_in *) 0); if ((*sockp < 0) || (connect (*sockp, (struct sockaddr *) raddr, sizeof (*raddr)) < 0)) { struct rpc_createerr *ce = &get_rpc_createerr (); ce->cf_stat = RPC_SYSTEMERROR; ce->cf_error.re_errno = errno; if (*sockp >= 0) (void) close (*sockp); goto fooy; } ct->ct_closeit = TRUE; } else { ct->ct_closeit = FALSE; } /* * Set up private data struct */ ct->ct_sock = *sockp; ct->ct_wait.tv_usec = 0; ct->ct_waitset = FALSE; ct->ct_addr = *raddr; /* * Initialize call message */ call_msg.rm_xid = _create_xid (); call_msg.rm_direction = CALL; call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; call_msg.rm_call.cb_prog = prog; call_msg.rm_call.cb_vers = vers; /* * pre-serialize the static part of the call msg and stash it away */ xdrmem_create (&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE, XDR_ENCODE); if (!xdr_callhdr (&(ct->ct_xdrs), &call_msg)) { if (ct->ct_closeit) { (void) close (*sockp); } goto fooy; } ct->ct_mpos = XDR_GETPOS (&(ct->ct_xdrs)); XDR_DESTROY (&(ct->ct_xdrs)); /* * Create a client handle which uses xdrrec for serialization * and authnone for authentication. */ xdrrec_create (&(ct->ct_xdrs), sendsz, recvsz, (caddr_t) ct, readtcp, writetcp); h->cl_ops = &tcp_ops; h->cl_private = (caddr_t) ct; h->cl_auth = authnone_create (); return h; fooy: /* * Something goofed, free stuff and barf */ mem_free ((caddr_t) ct, sizeof (struct ct_data)); mem_free ((caddr_t) h, sizeof (CLIENT)); return ((CLIENT *) NULL); }
int nfsmount(const char *spec, const char *node, int *flags, char **extra_opts, char **mount_opts, int running_bg) { static char *prev_bg_host; char hostdir[1024]; CLIENT *mclient; char *hostname; char *pathname; char *old_opts; char *mounthost=NULL; char new_opts[1024]; struct timeval total_timeout; enum clnt_stat clnt_stat; static struct nfs_mount_data data; char *opt, *opteq; int val; struct hostent *hp; struct sockaddr_in server_addr; struct sockaddr_in mount_server_addr; struct pmap* pm_mnt; int msock, fsock; struct timeval retry_timeout; union { struct fhstatus nfsv2; struct mountres3 nfsv3; } status; struct stat statbuf; char *s; int port; int mountport; int proto; int bg; int soft; int intr; int posix; int nocto; int noac; int nolock; int retry; int tcp; int mountprog; int mountvers; int nfsprog; int nfsvers; int retval; time_t t; time_t prevt; time_t timeout; find_kernel_nfs_mount_version(); retval = EX_FAIL; msock = fsock = -1; mclient = NULL; if (strlen(spec) >= sizeof(hostdir)) { bb_error_msg("excessively long host:dir argument"); goto fail; } strcpy(hostdir, spec); if ((s = strchr(hostdir, ':'))) { hostname = hostdir; pathname = s + 1; *s = '\0'; /* Ignore all but first hostname in replicated mounts until they can be fully supported. ([email protected]) */ if ((s = strchr(hostdir, ','))) { *s = '\0'; bb_error_msg("warning: multiple hostnames not supported"); } } else { bb_error_msg("directory to mount not in host:dir format"); goto fail; } server_addr.sin_family = AF_INET; #ifdef HAVE_inet_aton if (!inet_aton(hostname, &server_addr.sin_addr)) #endif { if ((hp = gethostbyname(hostname)) == NULL) { bb_herror_msg("%s", hostname); goto fail; } else { if (hp->h_length > sizeof(struct in_addr)) { bb_error_msg("got bad hp->h_length"); hp->h_length = sizeof(struct in_addr); } memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length); } } memcpy (&mount_server_addr, &server_addr, sizeof (mount_server_addr)); /* add IP address to mtab options for use when unmounting */ s = inet_ntoa(server_addr.sin_addr); old_opts = *extra_opts; if (!old_opts) old_opts = ""; if (strlen(old_opts) + strlen(s) + 10 >= sizeof(new_opts)) { bb_error_msg("excessively long option argument"); goto fail; } sprintf(new_opts, "%s%saddr=%s", old_opts, *old_opts ? "," : "", s); *extra_opts = bb_xstrdup(new_opts); /* Set default options. * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to * let the kernel decide. * timeo is filled in after we know whether it'll be TCP or UDP. */ memset(&data, 0, sizeof(data)); data.retrans = 3; data.acregmin = 3; data.acregmax = 60; data.acdirmin = 30; data.acdirmax = 60; #if NFS_MOUNT_VERSION >= 2 data.namlen = NAME_MAX; #endif bg = 0; soft = 0; intr = 0; posix = 0; nocto = 0; nolock = 0; noac = 0; retry = 10000; /* 10000 minutes ~ 1 week */ tcp = 0; mountprog = MOUNTPROG; mountvers = 0; port = 0; mountport = 0; nfsprog = NFS_PROGRAM; nfsvers = 0; /* parse options */ for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) { if ((opteq = strchr(opt, '='))) { val = atoi(opteq + 1); *opteq = '\0'; if (!strcmp(opt, "rsize")) data.rsize = val; else if (!strcmp(opt, "wsize")) data.wsize = val; else if (!strcmp(opt, "timeo")) data.timeo = val; else if (!strcmp(opt, "retrans")) data.retrans = val; else if (!strcmp(opt, "acregmin")) data.acregmin = val; else if (!strcmp(opt, "acregmax")) data.acregmax = val; else if (!strcmp(opt, "acdirmin")) data.acdirmin = val; else if (!strcmp(opt, "acdirmax")) data.acdirmax = val; else if (!strcmp(opt, "actimeo")) { data.acregmin = val; data.acregmax = val; data.acdirmin = val; data.acdirmax = val; } else if (!strcmp(opt, "retry")) retry = val; else if (!strcmp(opt, "port")) port = val; else if (!strcmp(opt, "mountport")) mountport = val; else if (!strcmp(opt, "mounthost")) mounthost=bb_xstrndup(opteq+1, strcspn(opteq+1," \t\n\r,")); else if (!strcmp(opt, "mountprog")) mountprog = val; else if (!strcmp(opt, "mountvers")) mountvers = val; else if (!strcmp(opt, "nfsprog")) nfsprog = val; else if (!strcmp(opt, "nfsvers") || !strcmp(opt, "vers")) nfsvers = val; else if (!strcmp(opt, "proto")) { if (!strncmp(opteq+1, "tcp", 3)) tcp = 1; else if (!strncmp(opteq+1, "udp", 3)) tcp = 0; else printf(_("Warning: Unrecognized proto= option.\n")); } else if (!strcmp(opt, "namlen")) { #if NFS_MOUNT_VERSION >= 2 if (nfs_mount_version >= 2) data.namlen = val; else #endif printf(_("Warning: Option namlen is not supported.\n")); } else if (!strcmp(opt, "addr")) /* ignore */; else { printf(_("unknown nfs mount parameter: " "%s=%d\n"), opt, val); goto fail; } } else { val = 1; if (!strncmp(opt, "no", 2)) { val = 0; opt += 2; } if (!strcmp(opt, "bg")) bg = val; else if (!strcmp(opt, "fg")) bg = !val; else if (!strcmp(opt, "soft")) soft = val; else if (!strcmp(opt, "hard")) soft = !val; else if (!strcmp(opt, "intr")) intr = val; else if (!strcmp(opt, "posix")) posix = val; else if (!strcmp(opt, "cto")) nocto = !val; else if (!strcmp(opt, "ac")) noac = !val; else if (!strcmp(opt, "tcp")) tcp = val; else if (!strcmp(opt, "udp")) tcp = !val; else if (!strcmp(opt, "lock")) { if (nfs_mount_version >= 3) nolock = !val; else printf(_("Warning: option nolock is not supported.\n")); } else { printf(_("unknown nfs mount option: " "%s%s\n"), val ? "" : "no", opt); goto fail; } } } proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP; data.flags = (soft ? NFS_MOUNT_SOFT : 0) | (intr ? NFS_MOUNT_INTR : 0) | (posix ? NFS_MOUNT_POSIX : 0) | (nocto ? NFS_MOUNT_NOCTO : 0) | (noac ? NFS_MOUNT_NOAC : 0); #if NFS_MOUNT_VERSION >= 2 if (nfs_mount_version >= 2) data.flags |= (tcp ? NFS_MOUNT_TCP : 0); #endif #if NFS_MOUNT_VERSION >= 3 if (nfs_mount_version >= 3) data.flags |= (nolock ? NFS_MOUNT_NONLM : 0); #endif if (nfsvers > MAX_NFSPROT) { bb_error_msg("NFSv%d not supported!", nfsvers); return 0; } if (mountvers > MAX_NFSPROT) { bb_error_msg("NFSv%d not supported!", nfsvers); return 0; } if (nfsvers && !mountvers) mountvers = (nfsvers < 3) ? 1 : nfsvers; if (nfsvers && nfsvers < mountvers) { mountvers = nfsvers; } /* Adjust options if none specified */ if (!data.timeo) data.timeo = tcp ? 70 : 7; #ifdef NFS_MOUNT_DEBUG printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n", data.rsize, data.wsize, data.timeo, data.retrans); printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n", data.acregmin, data.acregmax, data.acdirmin, data.acdirmax); printf("port = %d, bg = %d, retry = %d, flags = %.8x\n", port, bg, retry, data.flags); printf("mountprog = %d, mountvers = %d, nfsprog = %d, nfsvers = %d\n", mountprog, mountvers, nfsprog, nfsvers); printf("soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d\n", (data.flags & NFS_MOUNT_SOFT) != 0, (data.flags & NFS_MOUNT_INTR) != 0, (data.flags & NFS_MOUNT_POSIX) != 0, (data.flags & NFS_MOUNT_NOCTO) != 0, (data.flags & NFS_MOUNT_NOAC) != 0); #if NFS_MOUNT_VERSION >= 2 printf("tcp = %d\n", (data.flags & NFS_MOUNT_TCP) != 0); #endif #endif data.version = nfs_mount_version; *mount_opts = (char *) &data; if (*flags & MS_REMOUNT) return 0; /* * If the previous mount operation on the same host was * backgrounded, and the "bg" for this mount is also set, * give up immediately, to avoid the initial timeout. */ if (bg && !running_bg && prev_bg_host && strcmp(hostname, prev_bg_host) == 0) { if (retry > 0) retval = EX_BG; return retval; } /* create mount deamon client */ /* See if the nfs host = mount host. */ if (mounthost) { if (mounthost[0] >= '0' && mounthost[0] <= '9') { mount_server_addr.sin_family = AF_INET; mount_server_addr.sin_addr.s_addr = inet_addr(hostname); } else { if ((hp = gethostbyname(mounthost)) == NULL) { bb_herror_msg("%s", mounthost); goto fail; } else { if (hp->h_length > sizeof(struct in_addr)) { bb_error_msg("got bad hp->h_length?"); hp->h_length = sizeof(struct in_addr); } mount_server_addr.sin_family = AF_INET; memcpy(&mount_server_addr.sin_addr, hp->h_addr, hp->h_length); } } } /* * The following loop implements the mount retries. On the first * call, "running_bg" is 0. When the mount times out, and the * "bg" option is set, the exit status EX_BG will be returned. * For a backgrounded mount, there will be a second call by the * child process with "running_bg" set to 1. * * The case where the mount point is not present and the "bg" * option is set, is treated as a timeout. This is done to * support nested mounts. * * The "retry" count specified by the user is the number of * minutes to retry before giving up. * * Only the first error message will be displayed. */ retry_timeout.tv_sec = 3; retry_timeout.tv_usec = 0; total_timeout.tv_sec = 20; total_timeout.tv_usec = 0; timeout = time(NULL) + 60 * retry; prevt = 0; t = 30; val = 1; for (;;) { if (bg && stat(node, &statbuf) == -1) { if (running_bg) { sleep(val); /* 1, 2, 4, 8, 16, 30, ... */ val *= 2; if (val > 30) val = 30; } } else { /* be careful not to use too many CPU cycles */ if (t - prevt < 30) sleep(30); pm_mnt = get_mountport(&mount_server_addr, mountprog, mountvers, proto, mountport); /* contact the mount daemon via TCP */ mount_server_addr.sin_port = htons(pm_mnt->pm_port); msock = RPC_ANYSOCK; switch (pm_mnt->pm_prot) { case IPPROTO_UDP: mclient = clntudp_create(&mount_server_addr, pm_mnt->pm_prog, pm_mnt->pm_vers, retry_timeout, &msock); if (mclient) break; mount_server_addr.sin_port = htons(pm_mnt->pm_port); msock = RPC_ANYSOCK; case IPPROTO_TCP: mclient = clnttcp_create(&mount_server_addr, pm_mnt->pm_prog, pm_mnt->pm_vers, &msock, 0, 0); break; default: mclient = 0; } if (mclient) { /* try to mount hostname:pathname */ mclient->cl_auth = authunix_create_default(); /* make pointers in xdr_mountres3 NULL so * that xdr_array allocates memory for us */ memset(&status, 0, sizeof(status)); if (pm_mnt->pm_vers == 3) clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT, (xdrproc_t) xdr_dirpath, (caddr_t) &pathname, (xdrproc_t) xdr_mountres3, (caddr_t) &status, total_timeout); else clnt_stat = clnt_call(mclient, MOUNTPROC_MNT, (xdrproc_t) xdr_dirpath, (caddr_t) &pathname, (xdrproc_t) xdr_fhstatus, (caddr_t) &status, total_timeout); if (clnt_stat == RPC_SUCCESS) break; /* we're done */ if (errno != ECONNREFUSED) { clnt_perror(mclient, "mount"); goto fail; /* don't retry */ } if (!running_bg && prevt == 0) clnt_perror(mclient, "mount"); auth_destroy(mclient->cl_auth); clnt_destroy(mclient); mclient = 0; close(msock); } else { if (!running_bg && prevt == 0) clnt_pcreateerror("mount"); } prevt = t; } if (!bg) goto fail; if (!running_bg) { prev_bg_host = bb_xstrdup(hostname); if (retry > 0) retval = EX_BG; goto fail; } t = time(NULL); if (t >= timeout) goto fail; } nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers; if (nfsvers == 2) { if (status.nfsv2.fhs_status != 0) { bb_error_msg("%s:%s failed, reason given by server: %s", hostname, pathname, nfs_strerror(status.nfsv2.fhs_status)); goto fail; } memcpy(data.root.data, (char *) status.nfsv2.fhstatus_u.fhs_fhandle, NFS_FHSIZE); #if NFS_MOUNT_VERSION >= 4 data.root.size = NFS_FHSIZE; memcpy(data.old_root.data, (char *) status.nfsv2.fhstatus_u.fhs_fhandle, NFS_FHSIZE); #endif } else { #if NFS_MOUNT_VERSION >= 4 fhandle3 *my_fhandle; if (status.nfsv3.fhs_status != 0) { bb_error_msg("%s:%s failed, reason given by server: %s", hostname, pathname, nfs_strerror(status.nfsv3.fhs_status)); goto fail; } my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle; memset(data.old_root.data, 0, NFS_FHSIZE); memset(&data.root, 0, sizeof(data.root)); data.root.size = my_fhandle->fhandle3_len; memcpy(data.root.data, (char *) my_fhandle->fhandle3_val, my_fhandle->fhandle3_len); data.flags |= NFS_MOUNT_VER3; #endif } /* create nfs socket for kernel */ if (tcp) { if (nfs_mount_version < 3) { printf(_("NFS over TCP is not supported.\n")); goto fail; } fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); } else fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (fsock < 0) { perror(_("nfs socket")); goto fail; } if (bindresvport(fsock, 0) < 0) { perror(_("nfs bindresvport")); goto fail; } if (port == 0) { server_addr.sin_port = PMAPPORT; port = pmap_getport(&server_addr, nfsprog, nfsvers, tcp ? IPPROTO_TCP : IPPROTO_UDP); if (port == 0) port = NFS_PORT; #ifdef NFS_MOUNT_DEBUG else printf(_("used portmapper to find NFS port\n")); #endif } #ifdef NFS_MOUNT_DEBUG printf(_("using port %d for nfs deamon\n"), port); #endif server_addr.sin_port = htons(port); /* * connect() the socket for kernels 1.3.10 and below only, * to avoid problems with multihomed hosts. * --Swen */ if (get_kernel_revision() <= 66314 && connect(fsock, (struct sockaddr *) &server_addr, sizeof (server_addr)) < 0) { perror(_("nfs connect")); goto fail; } /* prepare data structure for kernel */ data.fd = fsock; memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr)); strncpy(data.hostname, hostname, sizeof(data.hostname)); /* clean up */ auth_destroy(mclient->cl_auth); clnt_destroy(mclient); close(msock); return 0; /* abort */ fail: if (msock != -1) { if (mclient) { auth_destroy(mclient->cl_auth); clnt_destroy(mclient); } close(msock); } if (fsock != -1) close(fsock); return retval; }