2 * Send NSM notify calls to all hosts listed in /var/lib/sm
4 * Copyright (C) 2004-2006 Olaf Kirch <okir@suse.de>
8 #include <sys/socket.h>
11 #include <sys/param.h>
12 #include <sys/syslog.h>
13 #include <arpa/inet.h>
27 #define BASEDIR "/var/lib/nfs"
30 #define _SM_STATE_PATH BASEDIR "/state"
31 #define _SM_DIR_PATH BASEDIR "/sm"
32 #define _SM_BAK_PATH _SM_DIR_PATH ".bak"
34 #define NSM_PROG 100024
35 #define NSM_PROGRAM 100024
39 #define NSM_MAX_TIMEOUT 120 /* don't make this too big */
40 #define MAXMSGSIZE 256
42 typedef struct sockaddr_storage nsm_address;
45 struct nsm_host * next;
56 static char nsm_hostname[256];
57 static uint32_t nsm_state;
58 static int opt_debug = 0;
59 static int opt_quiet = 0;
60 static int opt_update_state = 1;
61 static unsigned int opt_max_retry = 15 * 60;
62 static char * opt_srcaddr = 0;
63 static uint16_t opt_srcport = 0;
64 static int log_syslog = 0;
66 static unsigned int nsm_get_state(int);
67 static void notify(void);
68 static void notify_host(int, struct nsm_host *);
69 static void recv_reply(int);
70 static void backup_hosts(const char *, const char *);
71 static void get_hosts(const char *);
72 static void insert_host(struct nsm_host *);
73 struct nsm_host * find_host(uint32_t);
74 static int addr_parse(int, const char *, nsm_address *);
75 static int addr_get_port(nsm_address *);
76 static void addr_set_port(nsm_address *, int);
77 static int host_lookup(int, const char *, nsm_address *);
78 void nsm_log(int fac, const char *fmt, ...);
80 static struct nsm_host * hosts = NULL;
83 main(int argc, char **argv)
87 while ((c = getopt(argc, argv, "dm:np:v:q")) != -1) {
93 opt_max_retry = atoi(optarg) * 60;
99 opt_srcport = atoi(optarg);
102 opt_srcaddr = optarg;
113 usage: fprintf(stderr, "sm-notify [-d]\n");
118 strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1);
120 if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) {
121 perror("gethostname");
125 backup_hosts(_SM_DIR_PATH, _SM_BAK_PATH);
126 get_hosts(_SM_BAK_PATH);
130 printf("Backgrounding to notify hosts...\n");
132 openlog("sm-notify", LOG_PID, LOG_DAEMON);
135 if (daemon(0, 0) < 0) {
136 nsm_log(LOG_WARNING, "unable to background: %s",
146 /* Get and update the NSM state. This will call sync() */
147 nsm_state = nsm_get_state(opt_update_state);
154 while ((hp = hosts) != 0) {
157 "Unable to notify %s, giving up",
172 nsm_address local_addr;
176 sock = socket(AF_INET, SOCK_DGRAM, 0);
181 fcntl(sock, F_SETFL, O_NONBLOCK);
183 memset(&local_addr, 0, sizeof(local_addr));
184 local_addr.ss_family = AF_INET; /* Default to IPv4 */
186 /* Bind source IP if provided on command line */
188 if (!addr_parse(AF_INET, opt_srcaddr, &local_addr)
189 && !host_lookup(AF_INET, opt_srcaddr, &local_addr)) {
191 "Not a valid hostname or address: \"%s\"\n",
195 /* We know it's IPv4 at this point */
198 /* Use source port if provided on the command line,
199 * otherwise use bindresvport */
201 addr_set_port(&local_addr, opt_srcport);
202 if (bind(sock, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0) {
207 (void) bindresvport(sock, (struct sockaddr_in *) &local_addr);
211 failtime = time(NULL) + opt_max_retry;
215 time_t now = time(NULL);
216 unsigned int sent = 0;
220 if (failtime && now >= failtime)
223 while ((wait = hosts->send_next - now) <= 0) {
224 /* Never send more than 10 packets at once */
228 /* Remove queue head */
232 notify_host(sock, hp);
234 /* Set the timeout for this call, using an
235 exponential timeout strategy */
237 if ((hp->timeout <<= 1) > NSM_MAX_TIMEOUT)
238 hp->timeout = NSM_MAX_TIMEOUT;
239 hp->send_next = now + wait;
245 nsm_log(LOG_DEBUG, "Host %s due in %ld seconds",
254 if (poll(&pfd, 1, wait) != 1)
262 * Send notification to a single host
265 notify_host(int sock, struct nsm_host *host)
267 static unsigned int xid = 0;
269 uint32_t msgbuf[MAXMSGSIZE], *p;
273 xid = getpid() + time(NULL);
277 memset(msgbuf, 0, sizeof(msgbuf));
279 *p++ = htonl(host->xid);
283 /* If we retransmitted 4 times, reset the port to force
284 * a new portmap lookup (in case statd was restarted)
286 if (host->retries >= 4) {
287 addr_set_port(&host->addr, 0);
292 if (addr_get_port(&dest) == 0) {
293 /* Build a PMAP packet */
294 nsm_log(LOG_DEBUG, "Sending portmap query to %s", host->name);
296 addr_set_port(&dest, 111);
297 *p++ = htonl(100000);
305 *p++ = htonl(NSM_PROGRAM);
306 *p++ = htonl(NSM_VERSION);
307 *p++ = htonl(IPPROTO_UDP);
310 /* Build an SM_NOTIFY packet */
311 nsm_log(LOG_DEBUG, "Sending SM_NOTIFY to %s", host->name);
313 *p++ = htonl(NSM_PROGRAM);
314 *p++ = htonl(NSM_VERSION);
315 *p++ = htonl(NSM_NOTIFY);
322 len = strlen(nsm_hostname);
324 memcpy(p, nsm_hostname, len);
326 *p++ = htonl(nsm_state);
328 len = (p - msgbuf) << 2;
330 sendto(sock, msgbuf, len, 0, (struct sockaddr *) &dest, sizeof(dest));
334 * Receive reply from remote host
340 uint32_t msgbuf[MAXMSGSIZE], *p, *end;
344 res = recv(sock, msgbuf, sizeof(msgbuf), 0);
348 nsm_log(LOG_DEBUG, "Received packet...");
351 end = p + (res >> 2);
354 if (*p++ != htonl(1) /* must be REPLY */
355 || *p++ != htonl(0) /* must be ACCEPTED */
356 || *p++ != htonl(0) /* must be NULL verifier */
358 || *p++ != htonl(0)) /* must be SUCCESS */
361 /* Before we look at the data, find the host struct for
363 if ((hp = find_host(xid)) == NULL)
366 if (addr_get_port(&hp->addr) == 0) {
367 /* This was a portmap request */
374 hp->send_next = time(NULL);
376 /* No binding for statd. Delay the next
377 * portmap query for max timeout */
378 nsm_log(LOG_DEBUG, "No statd on %s", hp->name);
379 hp->timeout = NSM_MAX_TIMEOUT;
380 hp->send_next += NSM_MAX_TIMEOUT;
382 addr_set_port(&hp->addr, port);
383 if (hp->timeout >= NSM_MAX_TIMEOUT / 4)
384 hp->timeout = NSM_MAX_TIMEOUT / 4;
388 /* Successful NOTIFY call. Server returns void,
389 * so nothing we need to do here (except
390 * check that we didn't read past the end of the
394 nsm_log(LOG_DEBUG, "Host %s notified successfully", hp->name);
403 fail: /* Re-insert the host */
408 * Back up all hosts from the sm directory to sm.bak
411 backup_hosts(const char *dirname, const char *bakname)
416 if (!(dir = opendir(dirname))) {
421 while ((de = readdir(dir)) != NULL) {
422 char src[1024], dst[1024];
424 if (de->d_name[0] == '.')
427 snprintf(src, sizeof(src), "%s/%s", dirname, de->d_name);
428 snprintf(dst, sizeof(dst), "%s/%s", bakname, de->d_name);
429 if (rename(src, dst) < 0) {
431 "Failed to rename %s -> %s: %m",
439 * Get all entries from sm.bak and convert them to host names
442 get_hosts(const char *dirname)
444 struct nsm_host *host;
448 if (!(dir = opendir(dirname))) {
454 while ((de = readdir(dir)) != NULL) {
458 if (de->d_name[0] == '.')
461 host = calloc(1, sizeof(*host));
463 snprintf(path, sizeof(path), "%s/%s", dirname, de->d_name);
464 if (!addr_parse(AF_INET, de->d_name, &host->addr)
465 && !addr_parse(AF_INET6, de->d_name, &host->addr)
466 && !host_lookup(AF_INET, de->d_name, &host->addr)) {
468 "%s doesn't seem to be a valid address, skipped",
474 if (stat(path, &stb) < 0)
476 host->last_used = stb.st_mtime;
477 host->timeout = NSM_TIMEOUT;
478 host->path = strdup(path);
479 host->name = strdup(de->d_name);
491 * Insert host into sorted list
494 insert_host(struct nsm_host *host)
496 struct nsm_host **where, *p;
499 while ((p = *where) != 0) {
500 /* Sort in ascending order of timeout */
501 if (host->send_next < p->send_next)
503 /* If we have the same timeout, put the
504 * most recently used host first.
505 * This makes sure that "recent" hosts
506 * get notified first.
508 if (host->send_next == p->send_next
509 && host->last_used > p->last_used)
519 * Find host given the XID
522 find_host(uint32_t xid)
524 struct nsm_host **where, *p;
527 while ((p = *where) != 0) {
539 * Retrieve the current NSM state
542 nsm_get_state(int update)
544 char newfile[PATH_MAX];
547 if ((fd = open(_SM_STATE_PATH, O_RDONLY)) < 0) {
549 nsm_log(LOG_WARNING, "%s: %m", _SM_STATE_PATH);
550 nsm_log(LOG_WARNING, "Creating %s, set initial state 1",
556 if (read(fd, &state, sizeof(state)) != sizeof(state)) {
558 "%s: bad file size, setting state = 1",
571 snprintf(newfile, sizeof(newfile),
572 "%s.new", _SM_STATE_PATH);
573 if ((fd = open(newfile, O_CREAT|O_WRONLY, 0644)) < 0) {
574 nsm_log(LOG_WARNING, "Cannot create %s: %m", newfile);
577 if (write(fd, &state, sizeof(state)) != sizeof(state)) {
579 "Failed to write state to %s", newfile);
583 if (rename(newfile, _SM_STATE_PATH) < 0) {
585 "Cannot create %s: %m", _SM_STATE_PATH);
595 * Address handling utilities
598 addr_parse(int af, const char *name, nsm_address *addr)
603 ptr = &((struct sockaddr_in *) addr)->sin_addr;
604 else if (af == AF_INET6)
605 ptr = &((struct sockaddr_in6 *) addr)->sin6_addr;
608 if (inet_pton(af, name, ptr) <= 0)
610 ((struct sockaddr *) addr)->sa_family = af;
615 addr_get_port(nsm_address *addr)
617 switch (((struct sockaddr *) addr)->sa_family) {
619 return ntohs(((struct sockaddr_in *) addr)->sin_port);
621 return ntohs(((struct sockaddr_in6 *) addr)->sin6_port);
627 addr_set_port(nsm_address *addr, int port)
629 switch (((struct sockaddr *) addr)->sa_family) {
631 ((struct sockaddr_in *) addr)->sin_port = htons(port);
634 ((struct sockaddr_in6 *) addr)->sin6_port = htons(port);
639 host_lookup(int af, const char *name, nsm_address *addr)
641 struct addrinfo hints, *ai;
644 memset(&hints, 0, sizeof(hints));
645 hints.ai_family = af;
647 if (getaddrinfo(name, NULL, &hints, &ai) != 0)
650 if (ai->ai_addrlen < sizeof(*addr)) {
651 memcpy(addr, ai->ai_addr, ai->ai_addrlen);
663 nsm_log(int fac, const char *fmt, ...)
667 if (fac == LOG_DEBUG && !opt_debug)
672 vsyslog(fac, fmt, ap);
674 vfprintf(stderr, fmt, ap);