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 #ifndef HAVE_DECL_AI_ADDRCONFIG
38 #define AI_ADDRCONFIG 0
42 #define NSM_MAX_TIMEOUT 120 /* don't make this too big */
45 struct nsm_host * next;
55 static char nsm_hostname[256];
57 static int nsm_family = AF_INET;
58 static int opt_debug = 0;
59 static _Bool opt_update_state = true;
60 static unsigned int opt_max_retry = 15 * 60;
61 static char * opt_srcaddr = NULL;
62 static char * opt_srcport = NULL;
64 static void notify(const int sock);
65 static int notify_host(int, struct nsm_host *);
66 static void recv_reply(int);
67 static void insert_host(struct nsm_host *);
68 static struct nsm_host *find_host(uint32_t);
69 static int record_pid(void);
71 static struct nsm_host * hosts = NULL;
74 static struct addrinfo *
75 smn_lookup(const char *name)
77 struct addrinfo *ai = NULL;
78 struct addrinfo hint = {
79 .ai_flags = AI_ADDRCONFIG,
80 .ai_family = (nsm_family == AF_INET ? AF_INET: AF_UNSPEC),
81 .ai_protocol = (int)IPPROTO_UDP,
85 error = getaddrinfo(name, NULL, &hint, &ai);
87 xlog(D_GENERAL, "getaddrinfo(3): %s", gai_strerror(error));
95 static struct nsm_host *
96 smn_alloc_host(const char *hostname, const time_t timestamp)
98 struct nsm_host *host;
100 host = calloc(1, sizeof(*host));
104 host->name = strdup(hostname);
105 if (host->name == NULL) {
110 host->last_used = timestamp;
111 host->timeout = NSM_TIMEOUT;
112 host->retries = 100; /* force address retry */
117 xlog_warn("Unable to allocate memory");
121 static void smn_forget_host(struct nsm_host *host)
123 xlog(D_CALL, "Removing %s from notify list", host->name);
125 nsm_delete_notified_host(host->name);
129 freeaddrinfo(host->ai);
135 smn_get_host(const char *hostname,
136 __attribute__ ((unused)) const struct sockaddr *sap,
137 __attribute__ ((unused)) const struct mon *m,
138 const time_t timestamp)
140 struct nsm_host *host;
142 host = smn_alloc_host(hostname, timestamp);
147 xlog(D_GENERAL, "Added host %s to notify list", hostname);
151 #ifdef IPV6_SUPPORTED
152 static int smn_socket(void)
157 * Use an AF_INET socket if IPv6 is disabled on the
160 sock = socket(AF_INET6, SOCK_DGRAM, 0);
162 if (errno != EAFNOSUPPORT) {
163 xlog(L_ERROR, "Failed to create RPC socket: %m");
166 sock = socket(AF_INET, SOCK_DGRAM, 0);
168 xlog(L_ERROR, "Failed to create RPC socket: %m");
172 nsm_family = AF_INET6;
174 if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
175 xlog(L_ERROR, "fcntl(3) on RPC socket failed: %m");
180 * TI-RPC over IPv6 (udp6/tcp6) does not handle IPv4. However,
181 * since sm-notify open-codes all of its RPC support, it can
182 * use a single socket and let the local network stack provide
183 * the correct mapping between address families automatically.
184 * This is the same thing that is done in the kernel.
186 if (nsm_family == AF_INET6) {
188 socklen_t zerolen = (socklen_t)sizeof(zero);
190 if (setsockopt(sock, SOL_IPV6, IPV6_V6ONLY,
191 (char *)&zero, zerolen) == -1) {
192 xlog(L_ERROR, "setsockopt(3) on RPC socket failed: %m");
203 #else /* !IPV6_SUPPORTED */
204 static int smn_socket(void)
208 sock = socket(AF_INET, SOCK_DGRAM, 0);
210 xlog(L_ERROR, "Failed to create RPC socket: %m");
214 if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
215 xlog(L_ERROR, "fcntl(3) on RPC socket failed: %m");
222 #endif /* !IPV6_SUPPORTED */
225 * If admin specified a source address or srcport, then convert those
226 * to a sockaddr and return it. Otherwise, return an ANYADDR address.
229 static struct addrinfo *
230 smn_bind_address(const char *srcaddr, const char *srcport)
232 struct addrinfo *ai = NULL;
233 struct addrinfo hint = {
234 .ai_flags = AI_NUMERICSERV,
235 .ai_family = nsm_family,
236 .ai_protocol = (int)IPPROTO_UDP,
241 hint.ai_flags |= AI_PASSIVE;
244 error = getaddrinfo(srcaddr, "", &hint, &ai);
246 error = getaddrinfo(srcaddr, srcport, &hint, &ai);
249 "Invalid bind address or port for RPC socket: %s",
250 gai_strerror(error));
259 smn_bindresvport(int sock, struct sockaddr *sap)
261 return bindresvport_sa(sock, sap);
264 #else /* !HAVE_LIBTIRPC */
266 smn_bindresvport(int sock, struct sockaddr *sap)
268 if (sap->sa_family != AF_INET) {
269 errno = EAFNOSUPPORT;
273 return bindresvport(sock, (struct sockaddr_in *)(char *)sap);
275 #endif /* !HAVE_LIBTIRPC */
278 * Prepare a socket for sending RPC requests
280 * Returns a bound datagram socket file descriptor, or -1 if
284 smn_create_socket(const char *srcaddr, const char *srcport)
286 int sock, retry_cnt = 0;
294 ai = smn_bind_address(srcaddr, srcport);
300 /* Use source port if provided on the command line,
301 * otherwise use bindresvport */
303 if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
304 xlog(L_ERROR, "Failed to bind RPC socket: %m");
312 if (smn_bindresvport(sock, ai->ai_addr) == -1) {
314 "bindresvport on RPC socket failed: %m");
320 /* try to avoid known ports */
321 se = getservbyport((int)nfs_get_port(ai->ai_addr), "udp");
322 if (se != NULL && retry_cnt < 100) {
335 main(int argc, char **argv)
337 int c, sock, force = 0;
340 progname = strrchr(argv[0], '/');
341 if (progname != NULL)
346 while ((c = getopt(argc, argv, "dm:np:v:P:f")) != -1) {
355 opt_max_retry = atoi(optarg) * 60;
358 opt_update_state = false;
361 opt_srcport = optarg;
364 opt_srcaddr = optarg;
367 if (!nsm_setup_pathnames(argv[0], optarg))
377 usage: fprintf(stderr,
378 "Usage: %s -notify [-dfq] [-m max-retry-minutes] [-p srcport]\n"
379 " [-P /path/to/state/directory] [-v my_host_name]\n",
387 xlog_config(D_ALL, 1);
392 xlog(L_NOTICE, "Version " VERSION " starting");
394 if (nsm_is_default_parentdir()) {
395 if (record_pid() == 0 && force == 0 && opt_update_state) {
396 /* already run, don't try again */
397 xlog(L_NOTICE, "Already notifying clients; Exiting!");
403 strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1);
405 if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) {
406 xlog(L_ERROR, "Failed to obtain name of local host: %m");
410 (void)nsm_retire_monitored_hosts();
411 if (nsm_load_notify_list(smn_get_host) == 0) {
412 xlog(D_GENERAL, "No hosts to notify; exiting");
416 nsm_state = nsm_get_state(opt_update_state);
419 nsm_update_kernel_state(nsm_state);
422 xlog(L_NOTICE, "Backgrounding to notify hosts...\n");
424 if (daemon(0, 0) < 0) {
425 xlog(L_ERROR, "unable to background: %m");
434 sock = smn_create_socket(opt_srcaddr, opt_srcport);
438 if (!nsm_drop_privileges(-1))
446 while ((hp = hosts) != 0) {
448 xlog(L_NOTICE, "Unable to notify %s, giving up",
461 notify(const int sock)
466 failtime = time(NULL) + opt_max_retry;
470 time_t now = time(NULL);
471 unsigned int sent = 0;
475 if (failtime && now >= failtime)
478 while (hosts && ((wait = hosts->send_next - now) <= 0)) {
479 /* Never send more than 10 packets at once */
483 /* Remove queue head */
487 if (notify_host(sock, hp))
490 /* Set the timeout for this call, using an
491 exponential timeout strategy */
493 if ((hp->timeout <<= 1) > NSM_MAX_TIMEOUT)
494 hp->timeout = NSM_MAX_TIMEOUT;
495 hp->send_next = now + wait;
503 xlog(D_GENERAL, "Host %s due in %ld seconds",
512 if (poll(&pfd, 1, wait) != 1)
520 * Send notification to a single host
523 notify_host(int sock, struct nsm_host *host)
525 struct sockaddr *sap;
528 if (host->ai == NULL) {
529 host->ai = smn_lookup(host->name);
530 if (host->ai == NULL) {
531 xlog_warn("DNS resolution of %s failed; "
532 "retrying later", host->name);
537 /* If we retransmitted 4 times, reset the port to force
538 * a new portmap lookup (in case statd was restarted).
539 * We also rotate through multiple IP addresses at this
542 if (host->retries >= 4) {
543 /* don't rotate if there is only one addrinfo */
544 if (host->ai->ai_next != NULL) {
545 struct addrinfo *first = host->ai;
546 struct addrinfo **next = &host->ai;
548 /* remove the first entry from the list */
549 host->ai = first->ai_next;
550 first->ai_next = NULL;
551 /* find the end of the list */
552 next = &first->ai_next;
554 next = & (*next)->ai_next;
555 /* put first entry at end */
559 nfs_set_port(host->ai->ai_addr, 0);
563 sap = host->ai->ai_addr;
564 salen = host->ai->ai_addrlen;
566 if (nfs_get_port(sap) == 0)
567 host->xid = nsm_xmit_rpcbind(sock, sap, SM_PROG, SM_VERS);
569 host->xid = nsm_xmit_notify(sock, sap, salen,
570 SM_PROG, nsm_hostname, nsm_state);
576 * Extract the returned port number and set up the SM_NOTIFY call.
579 recv_rpcbind_reply(struct sockaddr *sap, struct nsm_host *host, XDR *xdr)
581 uint16_t port = nsm_recv_rpcbind(sap->sa_family, xdr);
583 host->send_next = time(NULL);
587 /* No binding for statd... */
588 xlog(D_GENERAL, "No statd on host %s", host->name);
589 host->timeout = NSM_MAX_TIMEOUT;
590 host->send_next += NSM_MAX_TIMEOUT;
592 nfs_set_port(sap, port);
593 if (host->timeout >= NSM_MAX_TIMEOUT / 4)
594 host->timeout = NSM_MAX_TIMEOUT / 4;
601 * Successful NOTIFY call. Server returns void, so nothing
602 * we need to do here.
605 recv_notify_reply(struct nsm_host *host)
607 xlog(D_GENERAL, "Host %s notified successfully", host->name);
609 smn_forget_host(host);
613 * Receive reply from remote host
619 struct sockaddr *sap;
620 char msgbuf[NSM_MAXMSGSIZE];
625 memset(msgbuf, 0 , sizeof(msgbuf));
626 msglen = recv(sock, msgbuf, sizeof(msgbuf), 0);
630 xlog(D_GENERAL, "Received packet...");
632 memset(&xdr, 0, sizeof(xdr));
633 xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE);
634 xid = nsm_parse_reply(&xdr);
638 /* Before we look at the data, find the host struct for
640 if ((hp = find_host(xid)) == NULL)
643 sap = hp->ai->ai_addr;
644 if (nfs_get_port(sap) == 0)
645 recv_rpcbind_reply(sap, hp, &xdr);
647 recv_notify_reply(hp);
654 * Insert host into sorted list
657 insert_host(struct nsm_host *host)
659 struct nsm_host **where, *p;
662 while ((p = *where) != 0) {
663 /* Sort in ascending order of timeout */
664 if (host->send_next < p->send_next)
666 /* If we have the same timeout, put the
667 * most recently used host first.
668 * This makes sure that "recent" hosts
669 * get notified first.
671 if (host->send_next == p->send_next
672 && host->last_used > p->last_used)
682 * Find host given the XID
684 static struct nsm_host *
685 find_host(uint32_t xid)
687 struct nsm_host **where, *p;
690 while ((p = *where) != 0) {
701 * Record pid in /var/run/sm-notify.pid
702 * This file should remain until a reboot, even if the
704 * If file already exists, fail.
706 static int record_pid(void)
712 (void)snprintf(pid, sizeof(pid), "%d\n", (int)getpid());
713 fd = open("/var/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600);
717 len = write(fd, pid, strlen(pid));
718 if ((len < 0) || ((size_t)len != strlen(pid))) {
719 xlog_warn("Writing to pid file failed: errno %d (%m)",