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
56 typedef struct sockaddr_storage nsm_address;
59 struct nsm_host * next;
71 static char nsm_hostname[256];
72 static uint32_t nsm_state;
73 static int opt_debug = 0;
74 static int opt_quiet = 0;
75 static int opt_update_state = 1;
76 static unsigned int opt_max_retry = 15 * 60;
77 static char * opt_srcaddr = 0;
78 static uint16_t opt_srcport = 0;
79 static int log_syslog = 0;
81 static unsigned int nsm_get_state(int);
82 static void notify(void);
83 static int notify_host(int, struct nsm_host *);
84 static void recv_reply(int);
85 static void backup_hosts(const char *, const char *);
86 static void get_hosts(const char *);
87 static void insert_host(struct nsm_host *);
88 struct nsm_host * find_host(uint32_t);
89 static int addr_get_port(nsm_address *);
90 static void addr_set_port(nsm_address *, int);
91 static struct addrinfo *host_lookup(int, const char *);
92 void nsm_log(int fac, const char *fmt, ...);
93 static int record_pid(void);
94 static void drop_privs(void);
95 static void set_kernel_nsm_state(int state);
97 static struct nsm_host * hosts = NULL;
100 main(int argc, char **argv)
105 while ((c = getopt(argc, argv, "dm:np:v:qP:f")) != -1) {
114 opt_max_retry = atoi(optarg) * 60;
117 opt_update_state = 0;
120 opt_srcport = atoi(optarg);
123 opt_srcaddr = optarg;
129 _SM_BASE_PATH = strdup(optarg);
130 _SM_STATE_PATH = malloc(strlen(optarg)+1+sizeof("state"));
131 _SM_DIR_PATH = malloc(strlen(optarg)+1+sizeof("sm"));
132 _SM_BAK_PATH = malloc(strlen(optarg)+1+sizeof("sm.bak"));
133 if (_SM_BASE_PATH == NULL ||
134 _SM_STATE_PATH == NULL ||
135 _SM_DIR_PATH == NULL ||
136 _SM_BAK_PATH == NULL) {
137 nsm_log(LOG_ERR, "unable to allocate memory");
140 strcat(strcpy(_SM_STATE_PATH, _SM_BASE_PATH), "/state");
141 strcat(strcpy(_SM_DIR_PATH, _SM_BASE_PATH), "/sm");
142 strcat(strcpy(_SM_BAK_PATH, _SM_BASE_PATH), "/sm.bak");
151 usage: fprintf(stderr,
152 "Usage: sm-notify [-dfq] [-m max-retry-minutes] [-p srcport]\n"
153 " [-P /path/to/state/directory] [-v my_host_name]\n");
157 if (strcmp(_SM_BASE_PATH, BASEDIR) == 0) {
158 if (record_pid() == 0 && force == 0 && opt_update_state == 1)
159 /* already run, don't try again */
164 strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1);
166 if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) {
167 nsm_log(LOG_ERR, "Failed to obtain name of local host: %s",
172 backup_hosts(_SM_DIR_PATH, _SM_BAK_PATH);
173 get_hosts(_SM_BAK_PATH);
175 /* Get and update the NSM state. This will call sync() */
176 nsm_state = nsm_get_state(opt_update_state);
177 set_kernel_nsm_state(nsm_state);
181 printf("Backgrounding to notify hosts...\n");
183 openlog("sm-notify", LOG_PID, LOG_DAEMON);
186 if (daemon(0, 0) < 0) {
187 nsm_log(LOG_ERR, "unable to background: %s",
202 while ((hp = hosts) != 0) {
205 "Unable to notify %s, giving up",
220 nsm_address local_addr;
226 sock = socket(AF_INET, SOCK_DGRAM, 0);
228 nsm_log(LOG_ERR, "Failed to create RPC socket: %s",
232 fcntl(sock, F_SETFL, O_NONBLOCK);
234 memset(&local_addr, 0, sizeof(local_addr));
235 local_addr.ss_family = AF_INET; /* Default to IPv4 */
237 /* Bind source IP if provided on command line */
239 struct addrinfo *ai = host_lookup(AF_INET, opt_srcaddr);
242 "Not a valid hostname or address: \"%s\"",
247 /* We know it's IPv4 at this point */
248 memcpy(&local_addr, ai->ai_addr, ai->ai_addrlen);
253 /* Use source port if provided on the command line,
254 * otherwise use bindresvport */
256 addr_set_port(&local_addr, opt_srcport);
257 if (bind(sock, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0) {
258 nsm_log(LOG_ERR, "Failed to bind RPC socket: %s",
264 struct sockaddr_in *sin = (struct sockaddr_in *)&local_addr;
265 (void) bindresvport(sock, sin);
266 /* try to avoid known ports */
267 se = getservbyport(sin->sin_port, "udp");
268 if (se && retry_cnt < 100) {
276 failtime = time(NULL) + opt_max_retry;
282 time_t now = time(NULL);
283 unsigned int sent = 0;
287 if (failtime && now >= failtime)
290 while (hosts && ((wait = hosts->send_next - now) <= 0)) {
291 /* Never send more than 10 packets at once */
295 /* Remove queue head */
299 if (notify_host(sock, hp)){
307 /* Set the timeout for this call, using an
308 exponential timeout strategy */
310 if ((hp->timeout <<= 1) > NSM_MAX_TIMEOUT)
311 hp->timeout = NSM_MAX_TIMEOUT;
312 hp->send_next = now + wait;
320 nsm_log(LOG_DEBUG, "Host %s due in %ld seconds",
329 if (poll(&pfd, 1, wait) != 1)
337 * Send notification to a single host
340 notify_host(int sock, struct nsm_host *host)
342 static unsigned int xid = 0;
344 uint32_t msgbuf[MAXMSGSIZE], *p;
348 xid = getpid() + time(NULL);
352 if (host->ai == NULL) {
353 host->ai = host_lookup(AF_UNSPEC, host->name);
354 if (host->ai == NULL) {
356 "%s doesn't seem to be a valid address,"
357 " skipped", host->name);
362 memset(msgbuf, 0, sizeof(msgbuf));
364 *p++ = htonl(host->xid);
368 /* If we retransmitted 4 times, reset the port to force
369 * a new portmap lookup (in case statd was restarted).
370 * We also rotate through multiple IP addresses at this
373 if (host->retries >= 4) {
374 struct addrinfo *first = host->ai;
375 struct addrinfo **next = &host->ai;
377 /* remove the first entry from the list */
378 host->ai = first->ai_next;
379 first->ai_next = NULL;
380 /* find the end of the list */
381 next = &first->ai_next;
383 next = & (*next)->ai_next;
384 /* put first entry at end */
386 memcpy(&host->addr, first->ai_addr, first->ai_addrlen);
387 addr_set_port(&host->addr, 0);
392 if (addr_get_port(&dest) == 0) {
393 /* Build a PMAP packet */
394 nsm_log(LOG_DEBUG, "Sending portmap query to %s", host->name);
396 addr_set_port(&dest, 111);
397 *p++ = htonl(100000);
405 *p++ = htonl(NSM_PROGRAM);
406 *p++ = htonl(NSM_VERSION);
407 *p++ = htonl(IPPROTO_UDP);
410 /* Build an SM_NOTIFY packet */
411 nsm_log(LOG_DEBUG, "Sending SM_NOTIFY to %s", host->name);
413 *p++ = htonl(NSM_PROGRAM);
414 *p++ = htonl(NSM_VERSION);
415 *p++ = htonl(NSM_NOTIFY);
422 len = strlen(nsm_hostname);
424 memcpy(p, nsm_hostname, len);
426 *p++ = htonl(nsm_state);
428 len = (p - msgbuf) << 2;
430 if (sendto(sock, msgbuf, len, 0, (struct sockaddr *) &dest, sizeof(dest)) < 0)
431 nsm_log(LOG_WARNING, "Sending Reboot Notification to "
432 "'%s' failed: errno %d (%s)", host->name, errno, strerror(errno));
438 * Receive reply from remote host
444 uint32_t msgbuf[MAXMSGSIZE], *p, *end;
448 res = recv(sock, msgbuf, sizeof(msgbuf), 0);
452 nsm_log(LOG_DEBUG, "Received packet...");
455 end = p + (res >> 2);
458 if (*p++ != htonl(1) /* must be REPLY */
459 || *p++ != htonl(0) /* must be ACCEPTED */
460 || *p++ != htonl(0) /* must be NULL verifier */
462 || *p++ != htonl(0)) /* must be SUCCESS */
465 /* Before we look at the data, find the host struct for
467 if ((hp = find_host(xid)) == NULL)
470 if (addr_get_port(&hp->addr) == 0) {
471 /* This was a portmap request */
478 hp->send_next = time(NULL);
480 /* No binding for statd. Delay the next
481 * portmap query for max timeout */
482 nsm_log(LOG_DEBUG, "No statd on %s", hp->name);
483 hp->timeout = NSM_MAX_TIMEOUT;
484 hp->send_next += NSM_MAX_TIMEOUT;
486 addr_set_port(&hp->addr, port);
487 if (hp->timeout >= NSM_MAX_TIMEOUT / 4)
488 hp->timeout = NSM_MAX_TIMEOUT / 4;
492 /* Successful NOTIFY call. Server returns void,
493 * so nothing we need to do here (except
494 * check that we didn't read past the end of the
498 nsm_log(LOG_DEBUG, "Host %s notified successfully",
504 freeaddrinfo(hp->ai);
509 fail: /* Re-insert the host */
514 * Back up all hosts from the sm directory to sm.bak
517 backup_hosts(const char *dirname, const char *bakname)
522 if (!(dir = opendir(dirname))) {
524 "Failed to open %s: %s", dirname, strerror(errno));
528 while ((de = readdir(dir)) != NULL) {
529 char src[1024], dst[1024];
531 if (de->d_name[0] == '.')
534 snprintf(src, sizeof(src), "%s/%s", dirname, de->d_name);
535 snprintf(dst, sizeof(dst), "%s/%s", bakname, de->d_name);
536 if (rename(src, dst) < 0) {
538 "Failed to rename %s -> %s: %m",
546 * Get all entries from sm.bak and convert them to host entries
549 get_hosts(const char *dirname)
551 struct nsm_host *host;
555 if (!(dir = opendir(dirname))) {
557 "Failed to open %s: %s", dirname, strerror(errno));
562 while ((de = readdir(dir)) != NULL) {
566 if (de->d_name[0] == '.')
569 host = calloc(1, sizeof(*host));
571 nsm_log(LOG_WARNING, "Unable to allocate memory");
575 snprintf(path, sizeof(path), "%s/%s", dirname, de->d_name);
576 if (stat(path, &stb) < 0)
579 host->last_used = stb.st_mtime;
580 host->timeout = NSM_TIMEOUT;
581 host->path = strdup(path);
582 host->name = strdup(de->d_name);
583 host->retries = 100; /* force address retry */
595 * Insert host into sorted list
598 insert_host(struct nsm_host *host)
600 struct nsm_host **where, *p;
603 while ((p = *where) != 0) {
604 /* Sort in ascending order of timeout */
605 if (host->send_next < p->send_next)
607 /* If we have the same timeout, put the
608 * most recently used host first.
609 * This makes sure that "recent" hosts
610 * get notified first.
612 if (host->send_next == p->send_next
613 && host->last_used > p->last_used)
623 * Find host given the XID
626 find_host(uint32_t xid)
628 struct nsm_host **where, *p;
631 while ((p = *where) != 0) {
643 * Retrieve the current NSM state
646 nsm_get_state(int update)
648 char newfile[PATH_MAX];
651 if ((fd = open(_SM_STATE_PATH, O_RDONLY)) < 0) {
653 nsm_log(LOG_WARNING, "%s: %m", _SM_STATE_PATH);
654 nsm_log(LOG_WARNING, "Creating %s, set initial state 1",
660 if (read(fd, &state, sizeof(state)) != sizeof(state)) {
662 "%s: bad file size, setting state = 1",
675 snprintf(newfile, sizeof(newfile),
676 "%s.new", _SM_STATE_PATH);
677 if ((fd = open(newfile, O_CREAT|O_WRONLY, 0644)) < 0) {
678 nsm_log(LOG_ERR, "Cannot create %s: %m", newfile);
681 if (write(fd, &state, sizeof(state)) != sizeof(state)) {
683 "Failed to write state to %s", newfile);
687 if (rename(newfile, _SM_STATE_PATH) < 0) {
689 "Cannot create %s: %m", _SM_STATE_PATH);
699 * Address handling utilities
703 addr_get_port(nsm_address *addr)
705 switch (((struct sockaddr *) addr)->sa_family) {
707 return ntohs(((struct sockaddr_in *) addr)->sin_port);
709 return ntohs(((struct sockaddr_in6 *) addr)->sin6_port);
715 addr_set_port(nsm_address *addr, int port)
717 switch (((struct sockaddr *) addr)->sa_family) {
719 ((struct sockaddr_in *) addr)->sin_port = htons(port);
722 ((struct sockaddr_in6 *) addr)->sin6_port = htons(port);
726 static struct addrinfo *
727 host_lookup(int af, const char *name)
729 struct addrinfo hints, *ai;
731 memset(&hints, 0, sizeof(hints));
732 hints.ai_family = af;
733 hints.ai_protocol = IPPROTO_UDP;
735 if (getaddrinfo(name, NULL, &hints, &ai) != 0)
745 nsm_log(int fac, const char *fmt, ...)
749 if (fac == LOG_DEBUG && !opt_debug)
754 vsyslog(fac, fmt, ap);
756 vfprintf(stderr, fmt, ap);
763 * Record pid in /var/run/sm-notify.pid
764 * This file should remain until a reboot, even if the
766 * If file already exists, fail.
768 static int record_pid(void)
773 snprintf(pid, 20, "%d\n", getpid());
774 fd = open("/var/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600);
777 write(fd, pid, strlen(pid));
782 /* Drop privileges to match owner of state-directory
783 * (in case a reply triggers some unknown bug).
785 static void drop_privs(void)
789 if (stat(_SM_DIR_PATH, &st) == -1 &&
790 stat(_SM_BASE_PATH, &st) == -1) {
795 if (st.st_uid == 0) {
797 "sm-notify running as root. chown %s to choose different user",
803 if (setgid(st.st_gid) == -1
804 || setuid(st.st_uid) == -1) {
805 nsm_log(LOG_ERR, "Fail to drop privileges");
810 static void set_kernel_nsm_state(int state)
814 fd = open("/proc/sys/fs/nfs/nsm_local_state",O_WRONLY);
817 snprintf(buf, sizeof(buf), "%d", state);
818 write(fd, buf, strlen(buf));