2 * Send NSM notify calls to all hosts listed in /var/lib/sm
4 * Copyright (C) 2004-2006 Olaf Kirch <okir@suse.de>
11 #include <sys/types.h>
12 #include <sys/socket.h>
15 #include <sys/param.h>
16 #include <sys/syslog.h>
17 #include <arpa/inet.h>
33 # define BASEDIR NFS_STATEDIR
35 # define BASEDIR "/var/lib/nfs"
39 #define DEFAULT_SM_STATE_PATH BASEDIR "/state"
40 #define DEFAULT_SM_DIR_PATH BASEDIR "/sm"
41 #define DEFAULT_SM_BAK_PATH DEFAULT_SM_DIR_PATH ".bak"
43 char *_SM_BASE_PATH = BASEDIR;
44 char *_SM_STATE_PATH = DEFAULT_SM_STATE_PATH;
45 char *_SM_DIR_PATH = DEFAULT_SM_DIR_PATH;
46 char *_SM_BAK_PATH = DEFAULT_SM_BAK_PATH;
48 #define NSM_PROG 100024
49 #define NSM_PROGRAM 100024
53 #define NSM_MAX_TIMEOUT 120 /* don't make this too big */
54 #define MAXMSGSIZE 256
57 struct nsm_host * next;
60 struct sockaddr_storage addr;
69 static char nsm_hostname[256];
70 static uint32_t nsm_state;
71 static int opt_debug = 0;
72 static int opt_quiet = 0;
73 static int opt_update_state = 1;
74 static unsigned int opt_max_retry = 15 * 60;
75 static char * opt_srcaddr = 0;
76 static uint16_t opt_srcport = 0;
77 static int log_syslog = 0;
79 static unsigned int nsm_get_state(int);
80 static void notify(void);
81 static int notify_host(int, struct nsm_host *);
82 static void recv_reply(int);
83 static void backup_hosts(const char *, const char *);
84 static void get_hosts(const char *);
85 static void insert_host(struct nsm_host *);
86 static struct nsm_host *find_host(uint32_t);
87 static void nsm_log(int fac, const char *fmt, ...);
88 static int record_pid(void);
89 static void drop_privs(void);
90 static void set_kernel_nsm_state(int state);
92 static struct nsm_host * hosts = NULL;
95 * Address handling utilities
98 static unsigned short smn_get_port(const struct sockaddr *sap)
100 switch (sap->sa_family) {
102 return ntohs(((struct sockaddr_in *)sap)->sin_port);
104 return ntohs(((struct sockaddr_in6 *)sap)->sin6_port);
109 static void smn_set_port(struct sockaddr *sap, const unsigned short port)
111 switch (sap->sa_family) {
113 ((struct sockaddr_in *)sap)->sin_port = htons(port);
116 ((struct sockaddr_in6 *)sap)->sin6_port = htons(port);
121 static struct addrinfo *smn_lookup(const char *name)
123 struct addrinfo *ai, hint = {
124 #if HAVE_DECL_AI_ADDRCONFIG
125 .ai_flags = AI_ADDRCONFIG,
126 #endif /* HAVE_DECL_AI_ADDRCONFIG */
127 .ai_family = AF_INET,
128 .ai_protocol = IPPROTO_UDP,
132 error = getaddrinfo(name, NULL, &hint, &ai);
138 nsm_log(LOG_ERR, "getaddrinfo(3): %s",
143 nsm_log(LOG_ERR, "getaddrinfo(3): %s",
144 gai_strerror(error));
150 static void smn_forget_host(struct nsm_host *host)
156 freeaddrinfo(host->ai);
162 main(int argc, char **argv)
167 while ((c = getopt(argc, argv, "dm:np:v:qP:f")) != -1) {
176 opt_max_retry = atoi(optarg) * 60;
179 opt_update_state = 0;
182 opt_srcport = atoi(optarg);
185 opt_srcaddr = optarg;
191 _SM_BASE_PATH = strdup(optarg);
192 _SM_STATE_PATH = malloc(strlen(optarg)+1+sizeof("state"));
193 _SM_DIR_PATH = malloc(strlen(optarg)+1+sizeof("sm"));
194 _SM_BAK_PATH = malloc(strlen(optarg)+1+sizeof("sm.bak"));
195 if (_SM_BASE_PATH == NULL ||
196 _SM_STATE_PATH == NULL ||
197 _SM_DIR_PATH == NULL ||
198 _SM_BAK_PATH == NULL) {
199 nsm_log(LOG_ERR, "unable to allocate memory");
202 strcat(strcpy(_SM_STATE_PATH, _SM_BASE_PATH), "/state");
203 strcat(strcpy(_SM_DIR_PATH, _SM_BASE_PATH), "/sm");
204 strcat(strcpy(_SM_BAK_PATH, _SM_BASE_PATH), "/sm.bak");
213 usage: fprintf(stderr,
214 "Usage: sm-notify [-dfq] [-m max-retry-minutes] [-p srcport]\n"
215 " [-P /path/to/state/directory] [-v my_host_name]\n");
220 openlog("sm-notify", LOG_PID, LOG_DAEMON);
222 if (strcmp(_SM_BASE_PATH, BASEDIR) == 0) {
223 if (record_pid() == 0 && force == 0 && opt_update_state == 1) {
224 /* already run, don't try again */
225 nsm_log(LOG_NOTICE, "Already notifying clients; Exiting!");
231 strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1);
233 if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) {
234 nsm_log(LOG_ERR, "Failed to obtain name of local host: %s",
239 backup_hosts(_SM_DIR_PATH, _SM_BAK_PATH);
240 get_hosts(_SM_BAK_PATH);
242 /* If there are not hosts to notify, just exit */
244 nsm_log(LOG_DEBUG, "No hosts to notify; exiting");
248 /* Get and update the NSM state. This will call sync() */
249 nsm_state = nsm_get_state(opt_update_state);
250 set_kernel_nsm_state(nsm_state);
254 printf("Backgrounding to notify hosts...\n");
256 if (daemon(0, 0) < 0) {
257 nsm_log(LOG_ERR, "unable to background: %s",
272 while ((hp = hosts) != 0) {
275 "Unable to notify %s, giving up",
290 struct sockaddr_storage address;
291 struct sockaddr *local_addr = (struct sockaddr *)&address;
297 sock = socket(AF_INET, SOCK_DGRAM, 0);
299 nsm_log(LOG_ERR, "Failed to create RPC socket: %s",
303 fcntl(sock, F_SETFL, O_NONBLOCK);
305 memset(&address, 0, sizeof(address));
306 local_addr->sa_family = AF_INET; /* Default to IPv4 */
308 /* Bind source IP if provided on command line */
310 struct addrinfo *ai = smn_lookup(opt_srcaddr);
313 "Not a valid hostname or address: \"%s\"",
318 /* We know it's IPv4 at this point */
319 memcpy(local_addr, ai->ai_addr, ai->ai_addrlen);
324 /* Use source port if provided on the command line,
325 * otherwise use bindresvport */
327 smn_set_port(local_addr, opt_srcport);
328 if (bind(sock, local_addr, sizeof(struct sockaddr_in)) < 0) {
329 nsm_log(LOG_ERR, "Failed to bind RPC socket: %s",
335 struct sockaddr_in *sin = (struct sockaddr_in *)local_addr;
336 (void) bindresvport(sock, sin);
337 /* try to avoid known ports */
338 se = getservbyport(sin->sin_port, "udp");
339 if (se && retry_cnt < 100) {
347 failtime = time(NULL) + opt_max_retry;
353 time_t now = time(NULL);
354 unsigned int sent = 0;
358 if (failtime && now >= failtime)
361 while (hosts && ((wait = hosts->send_next - now) <= 0)) {
362 /* Never send more than 10 packets at once */
366 /* Remove queue head */
370 if (notify_host(sock, hp))
373 /* Set the timeout for this call, using an
374 exponential timeout strategy */
376 if ((hp->timeout <<= 1) > NSM_MAX_TIMEOUT)
377 hp->timeout = NSM_MAX_TIMEOUT;
378 hp->send_next = now + wait;
386 nsm_log(LOG_DEBUG, "Host %s due in %ld seconds",
395 if (poll(&pfd, 1, wait) != 1)
403 * Send notification to a single host
406 notify_host(int sock, struct nsm_host *host)
408 struct sockaddr_storage address;
409 struct sockaddr *dest = (struct sockaddr *)&address;
410 socklen_t destlen = sizeof(address);
411 static unsigned int xid = 0;
412 uint32_t msgbuf[MAXMSGSIZE], *p;
416 xid = getpid() + time(NULL);
420 if (host->ai == NULL) {
421 host->ai = smn_lookup(host->name);
422 if (host->ai == NULL) {
424 "DNS resolution of %s failed; "
425 "retrying later", host->name);
430 memset(msgbuf, 0, sizeof(msgbuf));
432 *p++ = htonl(host->xid);
436 /* If we retransmitted 4 times, reset the port to force
437 * a new portmap lookup (in case statd was restarted).
438 * We also rotate through multiple IP addresses at this
441 if (host->retries >= 4) {
442 /* don't rotate if there is only one addrinfo */
443 if (host->ai->ai_next == NULL)
444 memcpy(&host->addr, host->ai->ai_addr,
445 host->ai->ai_addrlen);
447 struct addrinfo *first = host->ai;
448 struct addrinfo **next = &host->ai;
450 /* remove the first entry from the list */
451 host->ai = first->ai_next;
452 first->ai_next = NULL;
453 /* find the end of the list */
454 next = &first->ai_next;
456 next = & (*next)->ai_next;
457 /* put first entry at end */
459 memcpy(&host->addr, first->ai_addr,
463 smn_set_port((struct sockaddr *)&host->addr, 0);
467 memcpy(dest, &host->addr, destlen);
468 if (smn_get_port(dest) == 0) {
469 /* Build a PMAP packet */
470 nsm_log(LOG_DEBUG, "Sending portmap query to %s", host->name);
472 smn_set_port(dest, 111);
473 *p++ = htonl(100000);
481 *p++ = htonl(NSM_PROGRAM);
482 *p++ = htonl(NSM_VERSION);
483 *p++ = htonl(IPPROTO_UDP);
486 /* Build an SM_NOTIFY packet */
487 nsm_log(LOG_DEBUG, "Sending SM_NOTIFY to %s", host->name);
489 *p++ = htonl(NSM_PROGRAM);
490 *p++ = htonl(NSM_VERSION);
491 *p++ = htonl(NSM_NOTIFY);
498 len = strlen(nsm_hostname);
500 memcpy(p, nsm_hostname, len);
502 *p++ = htonl(nsm_state);
504 len = (p - msgbuf) << 2;
506 if (sendto(sock, msgbuf, len, 0, dest, destlen) < 0)
507 nsm_log(LOG_WARNING, "Sending Reboot Notification to "
508 "'%s' failed: errno %d (%s)", host->name, errno, strerror(errno));
514 * Receive reply from remote host
520 struct sockaddr *sap;
521 uint32_t msgbuf[MAXMSGSIZE], *p, *end;
525 res = recv(sock, msgbuf, sizeof(msgbuf), 0);
529 nsm_log(LOG_DEBUG, "Received packet...");
532 end = p + (res >> 2);
535 if (*p++ != htonl(1) /* must be REPLY */
536 || *p++ != htonl(0) /* must be ACCEPTED */
537 || *p++ != htonl(0) /* must be NULL verifier */
539 || *p++ != htonl(0)) /* must be SUCCESS */
542 /* Before we look at the data, find the host struct for
544 if ((hp = find_host(xid)) == NULL)
546 sap = (struct sockaddr *)&hp->addr;
548 if (smn_get_port(sap) == 0) {
549 /* This was a portmap request */
556 hp->send_next = time(NULL);
558 /* No binding for statd. Delay the next
559 * portmap query for max timeout */
560 nsm_log(LOG_DEBUG, "No statd on %s", hp->name);
561 hp->timeout = NSM_MAX_TIMEOUT;
562 hp->send_next += NSM_MAX_TIMEOUT;
564 smn_set_port(sap, port);
565 if (hp->timeout >= NSM_MAX_TIMEOUT / 4)
566 hp->timeout = NSM_MAX_TIMEOUT / 4;
570 /* Successful NOTIFY call. Server returns void,
571 * so nothing we need to do here (except
572 * check that we didn't read past the end of the
576 nsm_log(LOG_DEBUG, "Host %s notified successfully",
583 fail: /* Re-insert the host */
588 * Back up all hosts from the sm directory to sm.bak
591 backup_hosts(const char *dirname, const char *bakname)
596 if (!(dir = opendir(dirname))) {
598 "Failed to open %s: %s", dirname, strerror(errno));
602 while ((de = readdir(dir)) != NULL) {
603 char src[1024], dst[1024];
605 if (de->d_name[0] == '.')
608 snprintf(src, sizeof(src), "%s/%s", dirname, de->d_name);
609 snprintf(dst, sizeof(dst), "%s/%s", bakname, de->d_name);
610 if (rename(src, dst) < 0) {
612 "Failed to rename %s -> %s: %m",
620 * Get all entries from sm.bak and convert them to host entries
623 get_hosts(const char *dirname)
625 struct nsm_host *host;
629 if (!(dir = opendir(dirname))) {
631 "Failed to open %s: %s", dirname, strerror(errno));
636 while ((de = readdir(dir)) != NULL) {
640 if (de->d_name[0] == '.')
643 host = calloc(1, sizeof(*host));
645 nsm_log(LOG_WARNING, "Unable to allocate memory");
649 snprintf(path, sizeof(path), "%s/%s", dirname, de->d_name);
650 if (stat(path, &stb) < 0)
653 host->last_used = stb.st_mtime;
654 host->timeout = NSM_TIMEOUT;
655 host->path = strdup(path);
656 host->name = strdup(de->d_name);
657 host->retries = 100; /* force address retry */
669 * Insert host into sorted list
672 insert_host(struct nsm_host *host)
674 struct nsm_host **where, *p;
677 while ((p = *where) != 0) {
678 /* Sort in ascending order of timeout */
679 if (host->send_next < p->send_next)
681 /* If we have the same timeout, put the
682 * most recently used host first.
683 * This makes sure that "recent" hosts
684 * get notified first.
686 if (host->send_next == p->send_next
687 && host->last_used > p->last_used)
697 * Find host given the XID
699 static struct nsm_host *
700 find_host(uint32_t xid)
702 struct nsm_host **where, *p;
705 while ((p = *where) != 0) {
717 * Retrieve the current NSM state
720 nsm_get_state(int update)
722 char newfile[PATH_MAX];
725 if ((fd = open(_SM_STATE_PATH, O_RDONLY)) < 0) {
727 nsm_log(LOG_WARNING, "%s: %m", _SM_STATE_PATH);
728 nsm_log(LOG_WARNING, "Creating %s, set initial state 1",
734 if (read(fd, &state, sizeof(state)) != sizeof(state)) {
736 "%s: bad file size, setting state = 1",
749 snprintf(newfile, sizeof(newfile),
750 "%s.new", _SM_STATE_PATH);
751 if ((fd = open(newfile, O_CREAT|O_WRONLY, 0644)) < 0) {
752 nsm_log(LOG_ERR, "Cannot create %s: %m", newfile);
755 if (write(fd, &state, sizeof(state)) != sizeof(state)) {
757 "Failed to write state to %s", newfile);
761 if (rename(newfile, _SM_STATE_PATH) < 0) {
763 "Cannot create %s: %m", _SM_STATE_PATH);
776 nsm_log(int fac, const char *fmt, ...)
780 if (fac == LOG_DEBUG && !opt_debug)
785 vsyslog(fac, fmt, ap);
787 vfprintf(stderr, fmt, ap);
794 * Record pid in /var/run/sm-notify.pid
795 * This file should remain until a reboot, even if the
797 * If file already exists, fail.
799 static int record_pid(void)
804 snprintf(pid, 20, "%d\n", getpid());
805 fd = open("/var/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600);
808 if (write(fd, pid, strlen(pid)) != strlen(pid)) {
809 nsm_log(LOG_WARNING, "Writing to pid file failed: errno %d(%s)",
810 errno, strerror(errno));
816 /* Drop privileges to match owner of state-directory
817 * (in case a reply triggers some unknown bug).
819 static void drop_privs(void)
823 if (stat(_SM_DIR_PATH, &st) == -1 &&
824 stat(_SM_BASE_PATH, &st) == -1) {
829 if (st.st_uid == 0) {
831 "sm-notify running as root. chown %s to choose different user",
837 if (setgid(st.st_gid) == -1
838 || setuid(st.st_uid) == -1) {
839 nsm_log(LOG_ERR, "Fail to drop privileges");
844 static void set_kernel_nsm_state(int state)
847 const char *file = "/proc/sys/fs/nfs/nsm_local_state";
849 fd = open(file ,O_WRONLY);
852 snprintf(buf, sizeof(buf), "%d", state);
853 if (write(fd, buf, strlen(buf)) != strlen(buf)) {
854 nsm_log(LOG_WARNING, "Writing to '%s' failed: errno %d (%s)",
855 file, errno, strerror(errno));