| /* |
| * 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 <config.h> |
| #endif |
| |
| #include <sys/types.h> |
| #include <sys/ioctl.h> |
| #include <sys/stat.h> |
| #include <sys/socket.h> |
| #include <rpc/rpc.h> |
| #include <rpc/pmap_clnt.h> |
| #include <netinet/in.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <signal.h> |
| #include <fcntl.h> |
| #include <memory.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <time.h> |
| #include "nfslib.h" |
| #include "rpcmisc.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)); |
| close(sock); |
| 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); |
| } |
| } |