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;
76 while (loopcnt-- > 0) {
78 if (sockfd >= 0) close(sockfd);
80 if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
81 xlog(L_ERROR, "%s: Can't create socket: %m", __func__);
86 memset(&sin, 0, sizeof(sin));
87 sin.sin_family = AF_INET;
88 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
90 if (bindresvport(sockfd, &sin) < 0) {
91 xlog(D_GENERAL, "%s: can't bind to reserved port",
95 se = getservbyport(sin.sin_port, "udp");
98 /* rather not use that port, try again */
100 FD_SET(sockfd, &SVC_FDSET);
105 recv_rply(u_long *portp)
107 char msgbuf[NSM_MAXMSGSIZE];
109 notify_list *lp = NULL;
111 struct sockaddr_in sin;
112 socklen_t alen = (socklen_t)sizeof(sin);
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__);
123 memset(&xdr, 0, sizeof(xdr));
124 xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE);
125 xid = nsm_parse_reply(&xdr);
128 if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
129 struct in_addr addr = sin.sin_addr;
130 char buf[INET_ADDRSTRLEN];
132 xlog_warn("%s: Unrecognized reply from %s", __func__,
133 inet_ntop(AF_INET, &addr, buf,
134 (socklen_t)sizeof(buf)));
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 */
145 *portp = nsm_recv_getport(&xdr);
155 * Notify operation for a single list entry
158 process_entry(notify_list *lp)
160 struct sockaddr_in sin;
162 if (NL_TIMES(lp) == 0) {
163 xlog(D_GENERAL, "%s: Cannot notify localhost, giving up",
168 memset(&sin, 0, sizeof(sin));
169 sin.sin_family = AF_INET;
170 sin.sin_port = lp->port;
171 /* LH - moved address into switch */
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);
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));
184 memcpy(m.priv, NL_PRIV(lp), SM_PRIV_SIZE);
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);
192 lp->xid = nsm_xmit_nlmcall(sockfd,
193 (struct sockaddr *)(char *)&sin,
194 (socklen_t)sizeof(sin), &m, NL_STATE(lp));
197 xlog_warn("%s: failed to notify port %d",
198 __func__, ntohs(lp->port));
206 * Process a datagram received on the notify socket
209 process_reply(FD_SET_TYPE *rfds)
214 if (sockfd == -1 || !FD_ISSET(sockfd, rfds))
217 if (!(lp = recv_rply(&port)))
222 lp->port = htons((unsigned short) port);
224 NL_WHEN(lp) = time(NULL) + NOTIFY_TIMEOUT;
225 nlist_remove(¬ify, lp);
226 nlist_insert_timer(¬ify, lp);
229 xlog_warn("%s: service %d not registered on localhost",
230 __func__, NL_MY_PROG(lp));
232 xlog(D_GENERAL, "%s: Callback to %s (for %d) succeeded",
233 __func__, NL_MY_NAME(lp), NL_MON_NAME(lp));
235 nlist_free(¬ify, lp);
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
245 process_notify_list(void)
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(¬ify, entry);
254 nlist_insert_timer(¬ify, entry);
257 "%s: Can't callback %s (%d,%d), giving up",
262 nlist_free(¬ify, entry);