]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/statd/rmtcall.c
mounts.nfs: v2 and v3 background mounts should retry when server is down.
[nfs-utils.git] / utils / statd / rmtcall.c
1 /*
2  * Copyright (C) 1996, 1999 Olaf Kirch
3  * Modified by Jeffrey A. Uphoff, 1997-1999.
4  * Modified by H.J. Lu, 1998.
5  * Modified by Lon Hohberger, Oct. 2000
6  *   - Bugfix handling client responses.
7  *   - Paranoia on NOTIFY_CALLBACK case
8  *
9  * NSM for Linux.
10  */
11
12 /*
13  * After reboot, notify all hosts on our notify list. In order not to
14  * hang statd with delivery to dead hosts, we perform all RPC calls in
15  * parallel.
16  *
17  * It would have been nice to use the portmapper's rmtcall feature,
18  * but that's not possible for security reasons (the portmapper would
19  * have to forward the call with root privs for most statd's, which
20  * it won't if it's worth its money).
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <sys/time.h>
30 #include <netinet/in.h>
31 #include <net/if.h>
32 #include <arpa/inet.h>
33 #include <rpc/rpc.h>
34 #include <rpc/pmap_prot.h>
35 #include <rpc/pmap_rmt.h>
36 #include <time.h>
37 #include <netdb.h>
38 #include <string.h>
39 #include <unistd.h>
40
41 #include "sm_inter.h"
42 #include "statd.h"
43 #include "notlist.h"
44 #include "ha-callout.h"
45
46 #include "nsm.h"
47 #include "nfsrpc.h"
48
49 #if SIZEOF_SOCKLEN_T - 0 == 0
50 #define socklen_t int
51 #endif
52
53 static int              sockfd = -1;    /* notify socket */
54
55 /*
56  * Initialize socket used to notify lockd of peer reboots.
57  *
58  * Returns the file descriptor of the new socket if successful;
59  * otherwise returns -1 and logs an error.
60  *
61  * Lockd rejects such requests if the source port is not privileged.
62  * statd_get_socket() must be invoked while statd still holds root
63  * privileges in order for the socket to acquire a privileged source
64  * port.
65  */
66 int
67 statd_get_socket(void)
68 {
69         struct sockaddr_in      sin;
70         struct servent *se;
71         int loopcnt = 100;
72
73         if (sockfd >= 0)
74                 return sockfd;
75
76         while (loopcnt-- > 0) {
77
78                 if (sockfd >= 0) close(sockfd);
79
80                 if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
81                         xlog(L_ERROR, "%s: Can't create socket: %m", __func__);
82                         return -1;
83                 }
84
85
86                 memset(&sin, 0, sizeof(sin));
87                 sin.sin_family = AF_INET;
88                 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
89
90                 if (bindresvport(sockfd, &sin) < 0) {
91                         xlog(D_GENERAL, "%s: can't bind to reserved port",
92                                         __func__);
93                         break;
94                 }
95                 se = getservbyport(sin.sin_port, "udp");
96                 if (se == NULL)
97                         break;
98                 /* rather not use that port, try again */
99         }
100         FD_SET(sockfd, &SVC_FDSET);
101         return sockfd;
102 }
103
104 static notify_list *
105 recv_rply(u_long *portp)
106 {
107         char                    msgbuf[NSM_MAXMSGSIZE];
108         ssize_t                 msglen;
109         notify_list             *lp = NULL;
110         XDR                     xdr;
111         struct sockaddr_in      sin;
112         socklen_t               alen = (socklen_t)sizeof(sin);
113         uint32_t                xid;
114
115         memset(msgbuf, 0, sizeof(msgbuf));
116         msglen = recvfrom(sockfd, msgbuf, sizeof(msgbuf), 0,
117                                 (struct sockaddr *)(char *)&sin, &alen);
118         if (msglen == (ssize_t)-1) {
119                 xlog_warn("%s: recvfrom failed: %m", __func__);
120                 return NULL;
121         }
122
123         memset(&xdr, 0, sizeof(xdr));
124         xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE);
125         xid = nsm_parse_reply(&xdr);
126         if (xid == 0)
127                 goto done;
128         if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
129                 struct in_addr addr = sin.sin_addr;
130                 char buf[INET_ADDRSTRLEN];
131
132                 xlog_warn("%s: Unrecognized reply from %s", __func__,
133                                 inet_ntop(AF_INET, &addr, buf,
134                                                 (socklen_t)sizeof(buf)));
135                 goto done;
136         }
137
138         for (lp = notify; lp != NULL; lp = lp->next) {
139                 /* LH - this was a bug... it should have been checking
140                  * the xid from the response message from the client,
141                  * not the static, internal xid */
142                 if (lp->xid != xid)
143                         continue;
144                 if (lp->port == 0)
145                         *portp = nsm_recv_getport(&xdr);
146                 break;
147         }
148
149 done:
150         xdr_destroy(&xdr);
151         return lp;
152 }
153
154 /*
155  * Notify operation for a single list entry
156  */
157 static int
158 process_entry(notify_list *lp)
159 {
160         struct sockaddr_in      sin;
161
162         if (NL_TIMES(lp) == 0) {
163                 xlog(D_GENERAL, "%s: Cannot notify localhost, giving up",
164                                 __func__);
165                 return 0;
166         }
167
168         memset(&sin, 0, sizeof(sin));
169         sin.sin_family = AF_INET;
170         sin.sin_port   = lp->port;
171         /* LH - moved address into switch */
172
173         /* __FORCE__ loopback for callbacks to lockd ... */
174         /* Just in case we somehow ignored it thus far */
175         sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
176
177         if (sin.sin_port == 0)
178                 lp->xid = nsm_xmit_getport(sockfd, &sin,
179                                         (rpcprog_t)NL_MY_PROG(lp),
180                                         (rpcvers_t)NL_MY_VERS(lp));
181         else {
182                 struct mon m;
183
184                 memcpy(m.priv, NL_PRIV(lp), SM_PRIV_SIZE);
185
186                 m.mon_id.mon_name = NL_MON_NAME(lp);
187                 m.mon_id.my_id.my_name = NULL;
188                 m.mon_id.my_id.my_prog = NL_MY_PROG(lp);
189                 m.mon_id.my_id.my_vers = NL_MY_VERS(lp);
190                 m.mon_id.my_id.my_proc = NL_MY_PROC(lp);
191
192                 lp->xid = nsm_xmit_nlmcall(sockfd,
193                                 (struct sockaddr *)(char *)&sin,
194                                 (socklen_t)sizeof(sin), &m, NL_STATE(lp));
195         }
196         if (lp->xid == 0) {
197                 xlog_warn("%s: failed to notify port %d",
198                                 __func__, ntohs(lp->port));
199         }
200         NL_TIMES(lp) -= 1;
201
202         return 1;
203 }
204
205 /*
206  * Process a datagram received on the notify socket
207  */
208 int
209 process_reply(FD_SET_TYPE *rfds)
210 {
211         notify_list             *lp;
212         u_long                  port;
213
214         if (sockfd == -1 || !FD_ISSET(sockfd, rfds))
215                 return 0;
216
217         if (!(lp = recv_rply(&port)))
218                 return 1;
219
220         if (lp->port == 0) {
221                 if (port != 0) {
222                         lp->port = htons((unsigned short) port);
223                         process_entry(lp);
224                         NL_WHEN(lp) = time(NULL) + NOTIFY_TIMEOUT;
225                         nlist_remove(&notify, lp);
226                         nlist_insert_timer(&notify, lp);
227                         return 1;
228                 }
229                 xlog_warn("%s: service %d not registered on localhost",
230                         __func__, NL_MY_PROG(lp));
231         } else {
232                 xlog(D_GENERAL, "%s: Callback to %s (for %d) succeeded",
233                         __func__, NL_MY_NAME(lp), NL_MON_NAME(lp));
234         }
235         nlist_free(&notify, lp);
236         return 1;
237 }
238
239 /*
240  * Process a notify list, either for notifying remote hosts after reboot
241  * or for calling back (local) statd clients when the remote has notified
242  * us of a crash. 
243  */
244 int
245 process_notify_list(void)
246 {
247         notify_list     *entry;
248         time_t          now;
249
250         while ((entry = notify) != NULL && NL_WHEN(entry) < time(&now)) {
251                 if (process_entry(entry)) {
252                         NL_WHEN(entry) = time(NULL) + NOTIFY_TIMEOUT;
253                         nlist_remove(&notify, entry);
254                         nlist_insert_timer(&notify, entry);
255                 } else {
256                         xlog(L_ERROR,
257                                 "%s: Can't callback %s (%d,%d), giving up",
258                                         __func__,
259                                         NL_MY_NAME(entry),
260                                         NL_MY_PROG(entry),
261                                         NL_MY_VERS(entry));
262                         nlist_free(&notify, entry);
263                 }
264         }
265
266         return 1;
267 }