2 * Send NSM notify calls to all hosts listed in /var/lib/sm
4 * Copyright (C) 2004-2006 Olaf Kirch <okir@suse.de>
12 #include <sys/types.h>
13 #include <sys/socket.h>
16 #include <sys/param.h>
17 #include <sys/syslog.h>
18 #include <arpa/inet.h>
37 /* glibc before 2.3.4 */
38 #ifndef AI_NUMERICSERV
39 #define AI_NUMERICSERV 0
43 #define NSM_MAX_TIMEOUT 120 /* don't make this too big */
46 struct nsm_host * next;
58 static char nsm_hostname[SM_MAXSTRLEN + 1];
60 static int nsm_family = AF_INET;
61 static int opt_debug = 0;
62 static _Bool opt_update_state = true;
63 static unsigned int opt_max_retry = 15 * 60;
64 static char * opt_srcaddr = NULL;
65 static char * opt_srcport = NULL;
67 static void notify(const int sock);
68 static int notify_host(int, struct nsm_host *);
69 static void recv_reply(int);
70 static void insert_host(struct nsm_host *);
71 static struct nsm_host *find_host(uint32_t);
72 static int record_pid(void);
74 static struct nsm_host * hosts = NULL;
77 static struct addrinfo *
78 smn_lookup(const char *name)
80 struct addrinfo *ai = NULL;
81 struct addrinfo hint = {
82 .ai_family = (nsm_family == AF_INET ? AF_INET: AF_UNSPEC),
83 .ai_protocol = (int)IPPROTO_UDP,
87 error = getaddrinfo(name, NULL, &hint, &ai);
89 xlog(D_GENERAL, "getaddrinfo(3): %s", gai_strerror(error));
96 #ifdef HAVE_GETNAMEINFO
98 smn_get_hostname(const struct sockaddr *sap, const socklen_t salen,
101 char buf[NI_MAXHOST];
104 error = getnameinfo(sap, salen, buf, sizeof(buf), NULL, 0, NI_NAMEREQD);
106 xlog(L_ERROR, "my_name '%s' is unusable: %s",
107 name, gai_strerror(error));
112 #else /* !HAVE_GETNAMEINFO */
114 smn_get_hostname(const struct sockaddr *sap,
115 __attribute__ ((unused)) const socklen_t salen,
118 const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap;
119 const struct in_addr *addr = &sin->sin_addr;
122 if (sap->sa_family != AF_INET) {
123 xlog(L_ERROR, "my_name '%s' is unusable: Bad address family",
128 hp = gethostbyaddr(addr, (socklen_t)sizeof(addr), AF_INET);
130 xlog(L_ERROR, "my_name '%s' is unusable: %s",
131 name, hstrerror(h_errno));
134 return strdup(hp->h_name);
136 #endif /* !HAVE_GETNAMEINFO */
139 * Presentation addresses are converted to their canonical hostnames.
140 * If the IP address does not map to a hostname, it is an error:
141 * we never send a presentation address as the argument of SM_NOTIFY.
143 * If "name" is not a presentation address, it is left alone. This
144 * allows the administrator some flexibility if DNS isn't configured
145 * exactly how sm-notify prefers it.
147 * Returns NUL-terminated C string containing the result, or NULL
148 * if the canonical name doesn't exist or cannot be determined.
149 * The caller must free the result with free(3).
153 smn_verify_my_name(const char *name)
155 struct addrinfo *ai = NULL;
156 struct addrinfo hint = {
157 #ifdef IPV6_SUPPORTED
158 .ai_family = AF_UNSPEC,
159 #else /* !IPV6_SUPPORTED */
160 .ai_family = AF_INET,
161 #endif /* !IPV6_SUPPORTED */
162 .ai_flags = AI_NUMERICHOST,
167 error = getaddrinfo(name, NULL, &hint, &ai);
170 /* @name was a presentation address */
171 retval = smn_get_hostname(ai->ai_addr, ai->ai_addrlen, name);
177 /* @name was not a presentation address */
178 retval = strdup(name);
181 xlog(L_ERROR, "my_name '%s' is unusable: %s",
182 name, gai_strerror(error));
186 xlog(D_GENERAL, "Canonical name for my_name '%s': %s",
192 static struct nsm_host *
193 smn_alloc_host(const char *hostname, const char *mon_name,
194 const char *my_name, const time_t timestamp)
196 struct nsm_host *host;
198 host = calloc(1, sizeof(*host));
202 host->name = strdup(hostname);
203 host->mon_name = strdup(mon_name);
204 host->my_name = strdup(my_name);
205 if (host->name == NULL ||
206 host->mon_name == NULL ||
207 host->my_name == NULL) {
209 free(host->mon_name);
215 host->last_used = timestamp;
216 host->timeout = NSM_TIMEOUT;
217 host->retries = 100; /* force address retry */
222 xlog_warn("Unable to allocate memory");
226 static void smn_forget_host(struct nsm_host *host)
228 xlog(D_CALL, "Removing %s (%s, %s) from notify list",
229 host->name, host->mon_name, host->my_name);
231 nsm_delete_notified_host(host->name, host->mon_name, host->my_name);
234 free(host->mon_name);
237 freeaddrinfo(host->ai);
243 smn_get_host(const char *hostname,
244 __attribute__ ((unused)) const struct sockaddr *sap,
245 const struct mon *m, const time_t timestamp)
247 struct nsm_host *host;
249 host = smn_alloc_host(hostname,
250 m->mon_id.mon_name, m->mon_id.my_id.my_name, timestamp);
255 xlog(D_GENERAL, "Added host %s to notify list", hostname);
259 #ifdef IPV6_SUPPORTED
260 static int smn_socket(void)
265 * Use an AF_INET socket if IPv6 is disabled on the
268 sock = socket(AF_INET6, SOCK_DGRAM, 0);
270 if (errno != EAFNOSUPPORT) {
271 xlog(L_ERROR, "Failed to create RPC socket: %m");
274 sock = socket(AF_INET, SOCK_DGRAM, 0);
276 xlog(L_ERROR, "Failed to create RPC socket: %m");
280 nsm_family = AF_INET6;
282 if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
283 xlog(L_ERROR, "fcntl(3) on RPC socket failed: %m");
288 * TI-RPC over IPv6 (udp6/tcp6) does not handle IPv4. However,
289 * since sm-notify open-codes all of its RPC support, it can
290 * use a single socket and let the local network stack provide
291 * the correct mapping between address families automatically.
292 * This is the same thing that is done in the kernel.
294 if (nsm_family == AF_INET6) {
296 socklen_t zerolen = (socklen_t)sizeof(zero);
298 if (setsockopt(sock, SOL_IPV6, IPV6_V6ONLY,
299 (char *)&zero, zerolen) == -1) {
300 xlog(L_ERROR, "setsockopt(3) on RPC socket failed: %m");
311 #else /* !IPV6_SUPPORTED */
312 static int smn_socket(void)
316 sock = socket(AF_INET, SOCK_DGRAM, 0);
318 xlog(L_ERROR, "Failed to create RPC socket: %m");
322 if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
323 xlog(L_ERROR, "fcntl(3) on RPC socket failed: %m");
330 #endif /* !IPV6_SUPPORTED */
333 * If admin specified a source address or srcport, then convert those
334 * to a sockaddr and return it. Otherwise, return an ANYADDR address.
337 static struct addrinfo *
338 smn_bind_address(const char *srcaddr, const char *srcport)
340 struct addrinfo *ai = NULL;
341 struct addrinfo hint = {
342 .ai_flags = AI_NUMERICSERV,
343 .ai_family = nsm_family,
344 .ai_protocol = (int)IPPROTO_UDP,
349 hint.ai_flags |= AI_PASSIVE;
351 /* Do not allow "node" and "service" parameters both to be NULL */
353 error = getaddrinfo(srcaddr, "", &hint, &ai);
355 error = getaddrinfo(srcaddr, srcport, &hint, &ai);
358 "Invalid bind address or port for RPC socket: %s",
359 gai_strerror(error));
368 smn_bindresvport(int sock, struct sockaddr *sap)
370 return bindresvport_sa(sock, sap);
373 #else /* !HAVE_LIBTIRPC */
375 smn_bindresvport(int sock, struct sockaddr *sap)
377 if (sap->sa_family != AF_INET) {
378 errno = EAFNOSUPPORT;
382 return bindresvport(sock, (struct sockaddr_in *)(char *)sap);
384 #endif /* !HAVE_LIBTIRPC */
387 * Prepare a socket for sending RPC requests
389 * Returns a bound datagram socket file descriptor, or -1 if
393 smn_create_socket(const char *srcaddr, const char *srcport)
395 int sock, retry_cnt = 0;
403 ai = smn_bind_address(srcaddr, srcport);
409 /* Use source port if provided on the command line,
410 * otherwise use bindresvport */
412 if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
413 xlog(L_ERROR, "Failed to bind RPC socket: %m");
421 if (smn_bindresvport(sock, ai->ai_addr) == -1) {
423 "bindresvport on RPC socket failed: %m");
429 /* try to avoid known ports */
430 se = getservbyport((int)nfs_get_port(ai->ai_addr), "udp");
431 if (se != NULL && retry_cnt < 100) {
444 main(int argc, char **argv)
446 int c, sock, force = 0;
449 progname = strrchr(argv[0], '/');
450 if (progname != NULL)
455 while ((c = getopt(argc, argv, "dm:np:v:P:f")) != -1) {
464 opt_max_retry = atoi(optarg) * 60;
467 opt_update_state = false;
470 opt_srcport = optarg;
473 opt_srcaddr = optarg;
476 if (!nsm_setup_pathnames(argv[0], optarg))
486 usage: fprintf(stderr,
487 "Usage: %s -notify [-dfq] [-m max-retry-minutes] [-p srcport]\n"
488 " [-P /path/to/state/directory] [-v my_host_name]\n",
496 xlog_config(D_ALL, 1);
503 xlog(L_NOTICE, "Version " VERSION " starting");
505 if (nsm_is_default_parentdir()) {
506 if (record_pid() == 0 && force == 0 && opt_update_state) {
507 /* already run, don't try again */
508 xlog(L_NOTICE, "Already notifying clients; Exiting!");
513 if (opt_srcaddr != NULL) {
516 name = smn_verify_my_name(opt_srcaddr);
520 strncpy(nsm_hostname, name, sizeof(nsm_hostname));
524 (void)nsm_retire_monitored_hosts();
525 if (nsm_load_notify_list(smn_get_host) == 0) {
526 xlog(D_GENERAL, "No hosts to notify; exiting");
530 nsm_state = nsm_get_state(opt_update_state);
533 nsm_update_kernel_state(nsm_state);
536 xlog(L_NOTICE, "Backgrounding to notify hosts...\n");
538 if (daemon(0, 0) < 0) {
539 xlog(L_ERROR, "unable to background: %m");
548 sock = smn_create_socket(opt_srcaddr, opt_srcport);
552 if (!nsm_drop_privileges(-1))
560 while ((hp = hosts) != 0) {
562 xlog(L_NOTICE, "Unable to notify %s, giving up",
575 notify(const int sock)
580 failtime = time(NULL) + opt_max_retry;
584 time_t now = time(NULL);
585 unsigned int sent = 0;
589 if (failtime && now >= failtime)
592 while (hosts && ((wait = hosts->send_next - now) <= 0)) {
593 /* Never send more than 10 packets at once */
597 /* Remove queue head */
601 if (notify_host(sock, hp))
604 /* Set the timeout for this call, using an
605 exponential timeout strategy */
607 if ((hp->timeout <<= 1) > NSM_MAX_TIMEOUT)
608 hp->timeout = NSM_MAX_TIMEOUT;
609 hp->send_next = now + wait;
617 xlog(D_GENERAL, "Host %s due in %ld seconds",
626 if (poll(&pfd, 1, wait) != 1)
634 * Send notification to a single host
637 notify_host(int sock, struct nsm_host *host)
639 const char *my_name = (opt_srcaddr != NULL ?
640 nsm_hostname : host->my_name);
641 struct sockaddr *sap;
644 if (host->ai == NULL) {
645 host->ai = smn_lookup(host->name);
646 if (host->ai == NULL) {
647 xlog_warn("DNS resolution of %s failed; "
648 "retrying later", host->name);
653 /* If we retransmitted 4 times, reset the port to force
654 * a new portmap lookup (in case statd was restarted).
655 * We also rotate through multiple IP addresses at this
658 if (host->retries >= 4) {
659 /* don't rotate if there is only one addrinfo */
660 if (host->ai->ai_next != NULL) {
661 struct addrinfo *first = host->ai;
662 struct addrinfo **next = &host->ai;
664 /* remove the first entry from the list */
665 host->ai = first->ai_next;
666 first->ai_next = NULL;
667 /* find the end of the list */
668 next = &first->ai_next;
670 next = & (*next)->ai_next;
671 /* put first entry at end */
675 nfs_set_port(host->ai->ai_addr, 0);
679 sap = host->ai->ai_addr;
680 salen = host->ai->ai_addrlen;
682 if (nfs_get_port(sap) == 0)
683 host->xid = nsm_xmit_rpcbind(sock, sap, SM_PROG, SM_VERS);
685 host->xid = nsm_xmit_notify(sock, sap, salen,
686 SM_PROG, my_name, nsm_state);
692 * Extract the returned port number and set up the SM_NOTIFY call.
695 recv_rpcbind_reply(struct sockaddr *sap, struct nsm_host *host, XDR *xdr)
697 uint16_t port = nsm_recv_rpcbind(sap->sa_family, xdr);
699 host->send_next = time(NULL);
703 /* No binding for statd... */
704 xlog(D_GENERAL, "No statd on host %s", host->name);
705 host->timeout = NSM_MAX_TIMEOUT;
706 host->send_next += NSM_MAX_TIMEOUT;
708 nfs_set_port(sap, port);
709 if (host->timeout >= NSM_MAX_TIMEOUT / 4)
710 host->timeout = NSM_MAX_TIMEOUT / 4;
717 * Successful NOTIFY call. Server returns void.
719 * Try sending another SM_NOTIFY with an unqualified "my_name"
720 * argument. Reuse the port number. If "my_name" is already
721 * unqualified, we're done.
724 recv_notify_reply(struct nsm_host *host)
726 char *dot = strchr(host->my_name, '.');
730 host->send_next = time(NULL);
732 if (host->timeout >= NSM_MAX_TIMEOUT / 4)
733 host->timeout = NSM_MAX_TIMEOUT / 4;
736 xlog(D_GENERAL, "Host %s notified successfully", host->name);
737 smn_forget_host(host);
742 * Receive reply from remote host
748 struct sockaddr *sap;
749 char msgbuf[NSM_MAXMSGSIZE];
754 memset(msgbuf, 0 , sizeof(msgbuf));
755 msglen = recv(sock, msgbuf, sizeof(msgbuf), 0);
759 xlog(D_GENERAL, "Received packet...");
761 memset(&xdr, 0, sizeof(xdr));
762 xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE);
763 xid = nsm_parse_reply(&xdr);
767 /* Before we look at the data, find the host struct for
769 if ((hp = find_host(xid)) == NULL)
772 sap = hp->ai->ai_addr;
773 if (nfs_get_port(sap) == 0)
774 recv_rpcbind_reply(sap, hp, &xdr);
776 recv_notify_reply(hp);
783 * Insert host into sorted list
786 insert_host(struct nsm_host *host)
788 struct nsm_host **where, *p;
791 while ((p = *where) != 0) {
792 /* Sort in ascending order of timeout */
793 if (host->send_next < p->send_next)
795 /* If we have the same timeout, put the
796 * most recently used host first.
797 * This makes sure that "recent" hosts
798 * get notified first.
800 if (host->send_next == p->send_next
801 && host->last_used > p->last_used)
811 * Find host given the XID
813 static struct nsm_host *
814 find_host(uint32_t xid)
816 struct nsm_host **where, *p;
819 while ((p = *where) != 0) {
830 * Record pid in /var/run/sm-notify.pid
831 * This file should remain until a reboot, even if the
833 * If file already exists, fail.
835 static int record_pid(void)
841 (void)snprintf(pid, sizeof(pid), "%d\n", (int)getpid());
842 fd = open("/var/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600);
846 len = write(fd, pid, strlen(pid));
847 if ((len < 0) || ((size_t)len != strlen(pid))) {
848 xlog_warn("Writing to pid file failed: errno %d (%m)",