]> git.decadent.org.uk Git - nfs-utils.git/blob - support/nfs/rpcmisc.c
nfs-utils: whitespace clean ups in support/nfs/rpcmisc.c
[nfs-utils.git] / support / nfs / rpcmisc.c
1 /*
2  * Miscellaneous functions for RPC service startup and shutdown.
3  *
4  * This code is partially snarfed from rpcgen -s tcp -s udp,
5  * partly written by Mark Shand, Donald Becker, and Rick
6  * Sladkey. It was tweaked slightly by Olaf Kirch to be
7  * usable by both unfsd and mountd.
8  *
9  * This software may be used for any purpose provided
10  * the above copyright notice is retained.  It is supplied
11  * as is, with no warranty expressed or implied.
12  */
13
14 #ifdef HAVE_CONFIG_H
15 #include <config.h>
16 #endif
17
18 #include <sys/types.h>
19 #include <sys/ioctl.h>
20 #include <sys/stat.h>
21 #include <sys/socket.h>
22 #include <rpc/rpc.h>
23 #include <rpc/pmap_clnt.h>
24 #include <netinet/in.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <signal.h>
29 #include <fcntl.h>
30 #include <memory.h>
31 #include <errno.h>
32 #include <unistd.h>
33 #include <time.h>
34 #include "nfslib.h"
35
36 #if SIZEOF_SOCKLEN_T - 0 == 0
37 #define socklen_t int
38 #endif
39
40 static void     closedown(int sig);
41 static int      makesock(int port, int proto);
42
43 #define _RPCSVC_CLOSEDOWN       120
44 int     _rpcpmstart = 0;
45 int     _rpcfdtype = 0;
46 int     _rpcsvcdirty = 0;
47
48 void
49 rpc_init(char *name, int prog, int vers,
50          void (*dispatch)(struct svc_req *, register SVCXPRT *),
51          int defport)
52 {
53         struct sockaddr_in saddr;
54         SVCXPRT *transp;
55         int     sock;
56         socklen_t asize;
57
58         asize = sizeof(saddr);
59         sock = 0;
60         if (getsockname(0, (struct sockaddr *) &saddr, &asize) == 0
61             && saddr.sin_family == AF_INET) {
62                 socklen_t ssize = sizeof(int);
63                 int fdtype = 0;
64                 if (getsockopt(0, SOL_SOCKET, SO_TYPE,
65                                 (char *)&fdtype, &ssize) == -1)
66                         xlog(L_FATAL, "getsockopt failed: %s", strerror(errno));
67                 /* inetd passes a UDP socket or a listening TCP socket.
68                  * listen will fail on a connected TCP socket(passed by rsh).
69                  */
70                 if (!(fdtype == SOCK_STREAM && listen(0,5) == -1)) {
71                         _rpcfdtype = fdtype;
72                         _rpcpmstart = 1;
73                 }
74         }
75         if (!_rpcpmstart) {
76                 pmap_unset(prog, vers);
77                 sock = RPC_ANYSOCK;
78         }
79
80         if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_DGRAM)) {
81                 static SVCXPRT *last_transp = NULL;
82
83                 if (_rpcpmstart == 0) {
84                         if (last_transp
85                             && (!defport || defport == last_transp->xp_port)) {
86                                 transp = last_transp;
87                                 goto udp_transport;
88                         }
89                         if (defport == 0)
90                                 sock = RPC_ANYSOCK;
91                         else
92                                 sock = makesock(defport, IPPROTO_UDP);
93                 }
94                 if (sock == RPC_ANYSOCK)
95                         sock = svcudp_socket (prog, 1);
96                 transp = svcudp_create(sock);
97                 if (transp == NULL) {
98                         xlog(L_FATAL, "cannot create udp service.");
99                 }
100       udp_transport:
101                 if (!svc_register(transp, prog, vers, dispatch, IPPROTO_UDP)) {
102                         xlog(L_FATAL, "unable to register (%s, %d, udp).",
103                                         name, vers);
104                 }
105                 last_transp = transp;
106         }
107
108         if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_STREAM)) {
109                 static SVCXPRT *last_transp = NULL;
110
111                 if (_rpcpmstart == 0) {
112                         if (last_transp
113                             && (!defport || defport == last_transp->xp_port)) {
114                                 transp = last_transp;
115                                 goto tcp_transport;
116                         }
117                         if (defport == 0)
118                                 sock = RPC_ANYSOCK;
119                         else
120                                 sock = makesock(defport, IPPROTO_TCP);
121                 }
122                 if (sock == RPC_ANYSOCK)
123                         sock = svctcp_socket (prog, 1);
124                 transp = svctcp_create(sock, 0, 0);
125                 if (transp == NULL) {
126                         xlog(L_FATAL, "cannot create tcp service.");
127                 }
128       tcp_transport:
129                 if (!svc_register(transp, prog, vers, dispatch, IPPROTO_TCP)) {
130                         xlog(L_FATAL, "unable to register (%s, %d, tcp).",
131                                         name, vers);
132                 }
133                 last_transp = transp;
134         }
135
136         if (_rpcpmstart) {
137                 signal(SIGALRM, closedown);
138                 alarm(_RPCSVC_CLOSEDOWN);
139         }
140 }
141
142 static void closedown(sig)
143 int sig;
144 {
145         (void) signal(sig, closedown);
146         if (_rpcsvcdirty == 0) {
147                 static int size;
148                 int i, openfd;
149
150                 if (_rpcfdtype == SOCK_DGRAM)
151                         exit(0);
152                 if (size == 0) {
153                         size = getdtablesize();
154                 }
155                 for (i = 0, openfd = 0; i < size && openfd < 2; i++)
156                         if (FD_ISSET(i, &svc_fdset))
157                                 openfd++;
158                 if (openfd <= 1)
159                         exit(0);
160         }
161         (void) alarm(_RPCSVC_CLOSEDOWN);
162 }
163
164 /*
165  * Create listener socket for a given port
166  *
167  * Return an open network socket on success; otherwise return -1
168  * if some error occurs.
169  */
170 static int
171 makesock(int port, int proto)
172 {
173         struct sockaddr_in sin;
174         int     sock, sock_type, val;
175
176         sock_type = (proto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
177         sock = socket(AF_INET, sock_type, proto);
178         if (sock < 0) {
179                 xlog(L_FATAL, "Could not make a socket: %s",
180                                         strerror(errno));
181                 return -1;
182         }
183         memset((char *) &sin, 0, sizeof(sin));
184         sin.sin_family = AF_INET;
185         sin.sin_addr.s_addr = htonl(INADDR_ANY);
186         sin.sin_port = htons(port);
187
188         val = 1;
189         if (proto == IPPROTO_TCP)
190                 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
191                                &val, sizeof(val)) < 0)
192                         xlog(L_ERROR, "setsockopt failed: %s",
193                              strerror(errno));
194
195         if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) == -1) {
196                 xlog(L_FATAL, "Could not bind name to socket: %s",
197                                         strerror(errno));
198                 return -1;
199         }
200
201         return sock;
202 }