/* * Miscellaneous functions for RPC service startup and shutdown. * * This code is partially snarfed from rpcgen -s tcp -s udp, * partly written by Mark Shand, Donald Becker, and Rick * Sladkey. It was tweaked slightly by Olaf Kirch to be * usable by both unfsd and mountd. * * This software may be used for any purpose provided * the above copyright notice is retained. It is supplied * as is, with no warranty expressed or implied. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nfslib.h" #if SIZEOF_SOCKLEN_T - 0 == 0 #define socklen_t int #endif #define _RPCSVC_CLOSEDOWN 120 int _rpcpmstart = 0; unsigned int _rpcprotobits = (NFSCTL_UDPBIT|NFSCTL_TCPBIT); int _rpcsvcdirty = 0; static void closedown(int sig) { (void) signal(sig, closedown); if (_rpcsvcdirty == 0) { static int size; int i, openfd; if (NFSCTL_TCPISSET(_rpcprotobits) == 0) exit(0); if (size == 0) size = getdtablesize(); for (i = 0, openfd = 0; i < size && openfd < 2; i++) if (FD_ISSET(i, &svc_fdset)) openfd++; if (openfd <= 1) exit(0); } (void) alarm(_RPCSVC_CLOSEDOWN); } /* * Create listener socket for a given port * * Return an open network socket on success; otherwise return -1 * if some error occurs. */ static int makesock(int port, int proto) { struct sockaddr_in sin; int sock, sock_type, val; sock_type = (proto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM; sock = socket(AF_INET, sock_type, proto); if (sock < 0) { xlog(L_FATAL, "Could not make a socket: %s", strerror(errno)); return -1; } memset((char *) &sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_ANY); sin.sin_port = htons(port); val = 1; if (proto == IPPROTO_TCP) if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) xlog(L_ERROR, "setsockopt failed: %s", strerror(errno)); if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) == -1) { xlog(L_FATAL, "Could not bind name to socket: %s", strerror(errno)); return -1; } return svcsock_nonblock(sock); } void rpc_init(char *name, int prog, int vers, void (*dispatch)(struct svc_req *, register SVCXPRT *), int defport) { struct sockaddr_in saddr; SVCXPRT *transp; int sock; socklen_t asize; asize = sizeof(saddr); sock = 0; if (getsockname(0, (struct sockaddr *) &saddr, &asize) == 0 && saddr.sin_family == AF_INET) { socklen_t ssize = sizeof(int); int fdtype = 0; if (getsockopt(0, SOL_SOCKET, SO_TYPE, (char *)&fdtype, &ssize) == -1) xlog(L_FATAL, "getsockopt failed: %s", strerror(errno)); /* inetd passes a UDP socket or a listening TCP socket. * listen will fail on a connected TCP socket(passed by rsh). */ if (!(fdtype == SOCK_STREAM && listen(0,5) == -1)) { switch(fdtype) { case SOCK_DGRAM: NFSCTL_UDPSET(_rpcprotobits); break; case SOCK_STREAM: NFSCTL_TCPSET(_rpcprotobits); break; default: xlog(L_FATAL, "getsockopt returns bad socket type: %d", fdtype); } _rpcpmstart = 1; } } if (!_rpcpmstart) { pmap_unset(prog, vers); sock = RPC_ANYSOCK; } if (NFSCTL_UDPISSET(_rpcprotobits)) { static SVCXPRT *last_transp = NULL; if (_rpcpmstart == 0) { if (last_transp && (!defport || defport == last_transp->xp_port)) { transp = last_transp; goto udp_transport; } if (defport == 0) sock = RPC_ANYSOCK; else sock = makesock(defport, IPPROTO_UDP); } if (sock == RPC_ANYSOCK) sock = svcudp_socket (prog); transp = svcudp_create(sock); if (transp == NULL) { xlog(L_FATAL, "cannot create udp service."); } udp_transport: if (!svc_register(transp, prog, vers, dispatch, IPPROTO_UDP)) { xlog(L_FATAL, "unable to register (%s, %d, udp).", name, vers); } last_transp = transp; } if (NFSCTL_TCPISSET(_rpcprotobits)) { static SVCXPRT *last_transp = NULL; if (_rpcpmstart == 0) { if (last_transp && (!defport || defport == last_transp->xp_port)) { transp = last_transp; goto tcp_transport; } if (defport == 0) sock = RPC_ANYSOCK; else sock = makesock(defport, IPPROTO_TCP); } if (sock == RPC_ANYSOCK) sock = svctcp_socket (prog, 1); transp = svctcp_create(sock, 0, 0); if (transp == NULL) { xlog(L_FATAL, "cannot create tcp service."); } tcp_transport: if (!svc_register(transp, prog, vers, dispatch, IPPROTO_TCP)) { xlog(L_FATAL, "unable to register (%s, %d, tcp).", name, vers); } last_transp = transp; } if (_rpcpmstart) { signal(SIGALRM, closedown); alarm(_RPCSVC_CLOSEDOWN); } }