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
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
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).
27 #include <sys/types.h>
28 #include <sys/socket.h>
30 #include <netinet/in.h>
32 #include <arpa/inet.h>
34 #include <rpc/pmap_prot.h>
35 #include <rpc/pmap_rmt.h>
44 #include "ha-callout.h"
49 #if SIZEOF_SOCKLEN_T - 0 == 0
53 static int sockfd = -1; /* notify socket */
56 * Initialize socket used to notify lockd of peer reboots.
58 * Returns the file descriptor of the new socket if successful;
59 * otherwise returns -1 and logs an error.
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
67 statd_get_socket(void)
69 struct sockaddr_in sin;
71 const int loopcnt = 100;
72 int i, tmp_sockets[loopcnt];
77 for (i = 0; i < loopcnt; ++i) {
79 if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
80 xlog(L_ERROR, "%s: Can't create socket: %m", __func__);
84 memset(&sin, 0, sizeof(sin));
85 sin.sin_family = AF_INET;
86 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
88 if (bindresvport(sockfd, &sin) < 0) {
89 xlog(D_GENERAL, "%s: can't bind to reserved port",
93 se = getservbyport(sin.sin_port, "udp");
96 /* rather not use that port, try again */
98 tmp_sockets[i] = sockfd;
102 close(tmp_sockets[i]);
107 FD_SET(sockfd, &SVC_FDSET);
112 recv_rply(u_long *portp)
114 char msgbuf[NSM_MAXMSGSIZE];
116 notify_list *lp = NULL;
118 struct sockaddr_in sin;
119 socklen_t alen = (socklen_t)sizeof(sin);
122 memset(msgbuf, 0, sizeof(msgbuf));
123 msglen = recvfrom(sockfd, msgbuf, sizeof(msgbuf), 0,
124 (struct sockaddr *)(char *)&sin, &alen);
125 if (msglen == (ssize_t)-1) {
126 xlog_warn("%s: recvfrom failed: %m", __func__);
130 memset(&xdr, 0, sizeof(xdr));
131 xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE);
132 xid = nsm_parse_reply(&xdr);
135 if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
136 struct in_addr addr = sin.sin_addr;
137 char buf[INET_ADDRSTRLEN];
139 xlog_warn("%s: Unrecognized reply from %s", __func__,
140 inet_ntop(AF_INET, &addr, buf,
141 (socklen_t)sizeof(buf)));
145 for (lp = notify; lp != NULL; lp = lp->next) {
146 /* LH - this was a bug... it should have been checking
147 * the xid from the response message from the client,
148 * not the static, internal xid */
152 *portp = nsm_recv_getport(&xdr);
162 * Notify operation for a single list entry
165 process_entry(notify_list *lp)
167 struct sockaddr_in sin;
169 if (NL_TIMES(lp) == 0) {
170 xlog(D_GENERAL, "%s: Cannot notify localhost, giving up",
175 memset(&sin, 0, sizeof(sin));
176 sin.sin_family = AF_INET;
177 sin.sin_port = lp->port;
178 /* LH - moved address into switch */
180 /* __FORCE__ loopback for callbacks to lockd ... */
181 /* Just in case we somehow ignored it thus far */
182 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
184 if (sin.sin_port == 0)
185 lp->xid = nsm_xmit_getport(sockfd, &sin,
186 (rpcprog_t)NL_MY_PROG(lp),
187 (rpcvers_t)NL_MY_VERS(lp));
191 memcpy(m.priv, NL_PRIV(lp), SM_PRIV_SIZE);
193 m.mon_id.mon_name = NL_MON_NAME(lp);
194 m.mon_id.my_id.my_name = NULL;
195 m.mon_id.my_id.my_prog = NL_MY_PROG(lp);
196 m.mon_id.my_id.my_vers = NL_MY_VERS(lp);
197 m.mon_id.my_id.my_proc = NL_MY_PROC(lp);
199 lp->xid = nsm_xmit_nlmcall(sockfd,
200 (struct sockaddr *)(char *)&sin,
201 (socklen_t)sizeof(sin), &m, NL_STATE(lp));
204 xlog_warn("%s: failed to notify port %d",
205 __func__, ntohs(lp->port));
213 * Process a datagram received on the notify socket
216 process_reply(FD_SET_TYPE *rfds)
221 if (sockfd == -1 || !FD_ISSET(sockfd, rfds))
224 if (!(lp = recv_rply(&port)))
229 lp->port = htons((unsigned short) port);
231 NL_WHEN(lp) = time(NULL) + NOTIFY_TIMEOUT;
232 nlist_remove(¬ify, lp);
233 nlist_insert_timer(¬ify, lp);
236 xlog_warn("%s: service %d not registered on localhost",
237 __func__, NL_MY_PROG(lp));
239 xlog(D_GENERAL, "%s: Callback to %s (for %d) succeeded",
240 __func__, NL_MY_NAME(lp), NL_MON_NAME(lp));
242 nlist_free(¬ify, lp);
247 * Process a notify list, either for notifying remote hosts after reboot
248 * or for calling back (local) statd clients when the remote has notified
252 process_notify_list(void)
257 while ((entry = notify) != NULL && NL_WHEN(entry) < time(&now)) {
258 if (process_entry(entry)) {
259 NL_WHEN(entry) = time(NULL) + NOTIFY_TIMEOUT;
260 nlist_remove(¬ify, entry);
261 nlist_insert_timer(¬ify, entry);
264 "%s: Can't callback %s (%d,%d), giving up",
269 nlist_free(¬ify, entry);