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 #define NSM_MAX_TIMEOUT 120 /* don't make this too big */
40 struct nsm_host * next;
50 static char nsm_hostname[256];
52 static int opt_debug = 0;
53 static _Bool opt_update_state = true;
54 static unsigned int opt_max_retry = 15 * 60;
55 static char * opt_srcaddr = 0;
56 static uint16_t opt_srcport = 0;
58 static void notify(void);
59 static int notify_host(int, struct nsm_host *);
60 static void recv_reply(int);
61 static void insert_host(struct nsm_host *);
62 static struct nsm_host *find_host(uint32_t);
63 static int record_pid(void);
65 static struct nsm_host * hosts = NULL;
67 static struct addrinfo *smn_lookup(const char *name)
69 struct addrinfo *ai, hint = {
70 #if HAVE_DECL_AI_ADDRCONFIG
71 .ai_flags = AI_ADDRCONFIG,
72 #endif /* HAVE_DECL_AI_ADDRCONFIG */
74 .ai_protocol = IPPROTO_UDP,
78 error = getaddrinfo(name, NULL, &hint, &ai);
80 xlog(D_GENERAL, "getaddrinfo(3): %s", gai_strerror(error));
88 static struct nsm_host *
89 smn_alloc_host(const char *hostname, const time_t timestamp)
91 struct nsm_host *host;
93 host = calloc(1, sizeof(*host));
97 host->name = strdup(hostname);
98 if (host->name == NULL) {
103 host->last_used = timestamp;
104 host->timeout = NSM_TIMEOUT;
105 host->retries = 100; /* force address retry */
110 xlog_warn("Unable to allocate memory");
114 static void smn_forget_host(struct nsm_host *host)
116 xlog(D_CALL, "Removing %s from notify list", host->name);
118 nsm_delete_notified_host(host->name);
122 freeaddrinfo(host->ai);
128 smn_get_host(const char *hostname,
129 __attribute__ ((unused)) const struct sockaddr *sap,
130 __attribute__ ((unused)) const struct mon *m,
131 const time_t timestamp)
133 struct nsm_host *host;
135 host = smn_alloc_host(hostname, timestamp);
140 xlog(D_GENERAL, "Added host %s to notify list", hostname);
145 main(int argc, char **argv)
151 progname = strrchr(argv[0], '/');
152 if (progname != NULL)
157 while ((c = getopt(argc, argv, "dm:np:v:P:f")) != -1) {
166 opt_max_retry = atoi(optarg) * 60;
169 opt_update_state = false;
172 opt_srcport = atoi(optarg);
175 opt_srcaddr = optarg;
178 if (!nsm_setup_pathnames(argv[0], optarg))
188 usage: fprintf(stderr,
189 "Usage: %s -notify [-dfq] [-m max-retry-minutes] [-p srcport]\n"
190 " [-P /path/to/state/directory] [-v my_host_name]\n",
198 xlog_config(D_ALL, 1);
203 xlog(L_NOTICE, "Version " VERSION " starting");
205 if (nsm_is_default_parentdir()) {
206 if (record_pid() == 0 && force == 0 && opt_update_state) {
207 /* already run, don't try again */
208 xlog(L_NOTICE, "Already notifying clients; Exiting!");
214 strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1);
216 if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) {
217 xlog(L_ERROR, "Failed to obtain name of local host: %m");
221 (void)nsm_retire_monitored_hosts();
222 if (nsm_load_notify_list(smn_get_host) == 0) {
223 xlog(D_GENERAL, "No hosts to notify; exiting");
227 nsm_state = nsm_get_state(opt_update_state);
230 nsm_update_kernel_state(nsm_state);
233 xlog(L_NOTICE, "Backgrounding to notify hosts...\n");
235 if (daemon(0, 0) < 0) {
236 xlog(L_ERROR, "unable to background: %m");
250 while ((hp = hosts) != 0) {
252 xlog(L_NOTICE, "Unable to notify %s, giving up",
267 struct sockaddr_storage address;
268 struct sockaddr *local_addr = (struct sockaddr *)&address;
274 sock = socket(AF_INET, SOCK_DGRAM, 0);
276 xlog(L_ERROR, "Failed to create RPC socket: %m");
279 fcntl(sock, F_SETFL, O_NONBLOCK);
281 memset(&address, 0, sizeof(address));
282 local_addr->sa_family = AF_INET; /* Default to IPv4 */
284 /* Bind source IP if provided on command line */
286 struct addrinfo *ai = smn_lookup(opt_srcaddr);
289 "Not a valid hostname or address: \"%s\"",
294 /* We know it's IPv4 at this point */
295 memcpy(local_addr, ai->ai_addr, ai->ai_addrlen);
300 /* Use source port if provided on the command line,
301 * otherwise use bindresvport */
303 nfs_set_port(local_addr, opt_srcport);
304 if (bind(sock, local_addr, sizeof(struct sockaddr_in)) < 0) {
305 xlog(L_ERROR, "Failed to bind RPC socket: %m");
310 struct sockaddr_in *sin = (struct sockaddr_in *)local_addr;
311 (void) bindresvport(sock, sin);
312 /* try to avoid known ports */
313 se = getservbyport(sin->sin_port, "udp");
314 if (se && retry_cnt < 100) {
322 failtime = time(NULL) + opt_max_retry;
324 if (!nsm_drop_privileges(-1))
329 time_t now = time(NULL);
330 unsigned int sent = 0;
334 if (failtime && now >= failtime)
337 while (hosts && ((wait = hosts->send_next - now) <= 0)) {
338 /* Never send more than 10 packets at once */
342 /* Remove queue head */
346 if (notify_host(sock, hp))
349 /* Set the timeout for this call, using an
350 exponential timeout strategy */
352 if ((hp->timeout <<= 1) > NSM_MAX_TIMEOUT)
353 hp->timeout = NSM_MAX_TIMEOUT;
354 hp->send_next = now + wait;
362 xlog(D_GENERAL, "Host %s due in %ld seconds",
371 if (poll(&pfd, 1, wait) != 1)
379 * Send notification to a single host
382 notify_host(int sock, struct nsm_host *host)
384 struct sockaddr *sap;
387 if (host->ai == NULL) {
388 host->ai = smn_lookup(host->name);
389 if (host->ai == NULL) {
390 xlog_warn("DNS resolution of %s failed; "
391 "retrying later", host->name);
396 /* If we retransmitted 4 times, reset the port to force
397 * a new portmap lookup (in case statd was restarted).
398 * We also rotate through multiple IP addresses at this
401 if (host->retries >= 4) {
402 /* don't rotate if there is only one addrinfo */
403 if (host->ai->ai_next != NULL) {
404 struct addrinfo *first = host->ai;
405 struct addrinfo **next = &host->ai;
407 /* remove the first entry from the list */
408 host->ai = first->ai_next;
409 first->ai_next = NULL;
410 /* find the end of the list */
411 next = &first->ai_next;
413 next = & (*next)->ai_next;
414 /* put first entry at end */
418 nfs_set_port(host->ai->ai_addr, 0);
422 sap = host->ai->ai_addr;
423 salen = host->ai->ai_addrlen;
425 if (nfs_get_port(sap) == 0)
426 host->xid = nsm_xmit_rpcbind(sock, sap, SM_PROG, SM_VERS);
428 host->xid = nsm_xmit_notify(sock, sap, salen,
429 SM_PROG, nsm_hostname, nsm_state);
435 * Extract the returned port number and set up the SM_NOTIFY call.
438 recv_rpcbind_reply(struct sockaddr *sap, struct nsm_host *host, XDR *xdr)
440 uint16_t port = nsm_recv_rpcbind(sap->sa_family, xdr);
442 host->send_next = time(NULL);
446 /* No binding for statd... */
447 xlog(D_GENERAL, "No statd on host %s", host->name);
448 host->timeout = NSM_MAX_TIMEOUT;
449 host->send_next += NSM_MAX_TIMEOUT;
451 nfs_set_port(sap, port);
452 if (host->timeout >= NSM_MAX_TIMEOUT / 4)
453 host->timeout = NSM_MAX_TIMEOUT / 4;
460 * Successful NOTIFY call. Server returns void, so nothing
461 * we need to do here.
464 recv_notify_reply(struct nsm_host *host)
466 xlog(D_GENERAL, "Host %s notified successfully", host->name);
468 smn_forget_host(host);
472 * Receive reply from remote host
478 struct sockaddr *sap;
479 char msgbuf[NSM_MAXMSGSIZE];
484 memset(msgbuf, 0 , sizeof(msgbuf));
485 msglen = recv(sock, msgbuf, sizeof(msgbuf), 0);
489 xlog(D_GENERAL, "Received packet...");
491 memset(&xdr, 0, sizeof(xdr));
492 xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE);
493 xid = nsm_parse_reply(&xdr);
497 /* Before we look at the data, find the host struct for
499 if ((hp = find_host(xid)) == NULL)
502 sap = hp->ai->ai_addr;
503 if (nfs_get_port(sap) == 0)
504 recv_rpcbind_reply(sap, hp, &xdr);
506 recv_notify_reply(hp);
513 * Insert host into sorted list
516 insert_host(struct nsm_host *host)
518 struct nsm_host **where, *p;
521 while ((p = *where) != 0) {
522 /* Sort in ascending order of timeout */
523 if (host->send_next < p->send_next)
525 /* If we have the same timeout, put the
526 * most recently used host first.
527 * This makes sure that "recent" hosts
528 * get notified first.
530 if (host->send_next == p->send_next
531 && host->last_used > p->last_used)
541 * Find host given the XID
543 static struct nsm_host *
544 find_host(uint32_t xid)
546 struct nsm_host **where, *p;
549 while ((p = *where) != 0) {
560 * Record pid in /var/run/sm-notify.pid
561 * This file should remain until a reboot, even if the
563 * If file already exists, fail.
565 static int record_pid(void)
571 (void)snprintf(pid, sizeof(pid), "%d\n", (int)getpid());
572 fd = open("/var/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600);
576 len = write(fd, pid, strlen(pid));
577 if ((len < 0) || ((size_t)len != strlen(pid))) {
578 xlog_warn("Writing to pid file failed: errno %d (%m)",