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 DEFAULT_SM_STATE_PATH BASEDIR "/state"
31 #define DEFAULT_SM_DIR_PATH BASEDIR "/sm"
32 #define DEFAULT_SM_BAK_PATH DEFAULT_SM_DIR_PATH ".bak"
34 char *_SM_BASE_PATH = BASEDIR;
35 char *_SM_STATE_PATH = DEFAULT_SM_STATE_PATH;
36 char *_SM_DIR_PATH = DEFAULT_SM_DIR_PATH;
37 char *_SM_BAK_PATH = DEFAULT_SM_BAK_PATH;
39 #define NSM_PROG 100024
40 #define NSM_PROGRAM 100024
44 #define NSM_MAX_TIMEOUT 120 /* don't make this too big */
45 #define MAXMSGSIZE 256
47 typedef struct sockaddr_storage nsm_address;
50 struct nsm_host * next;
61 static char nsm_hostname[256];
62 static uint32_t nsm_state;
63 static int opt_debug = 0;
64 static int opt_quiet = 0;
65 static int opt_update_state = 1;
66 static unsigned int opt_max_retry = 15 * 60;
67 static char * opt_srcaddr = 0;
68 static uint16_t opt_srcport = 0;
69 static int log_syslog = 0;
71 static unsigned int nsm_get_state(int);
72 static void notify(void);
73 static void notify_host(int, struct nsm_host *);
74 static void recv_reply(int);
75 static void backup_hosts(const char *, const char *);
76 static void get_hosts(const char *);
77 static void insert_host(struct nsm_host *);
78 struct nsm_host * find_host(uint32_t);
79 static int addr_get_port(nsm_address *);
80 static void addr_set_port(nsm_address *, int);
81 static int host_lookup(int, const char *, nsm_address *);
82 void nsm_log(int fac, const char *fmt, ...);
83 static int record_pid();
84 static void drop_privs(void);
86 static struct nsm_host * hosts = NULL;
89 main(int argc, char **argv)
94 while ((c = getopt(argc, argv, "dm:np:v:qP:f")) != -1) {
103 opt_max_retry = atoi(optarg) * 60;
106 opt_update_state = 0;
109 opt_srcport = atoi(optarg);
112 opt_srcaddr = optarg;
118 _SM_BASE_PATH = strdup(optarg);
119 _SM_STATE_PATH = malloc(strlen(optarg)+1+sizeof("state"));
120 _SM_DIR_PATH = malloc(strlen(optarg)+1+sizeof("sm"));
121 _SM_BAK_PATH = malloc(strlen(optarg)+1+sizeof("sm.bak"));
122 if (_SM_BASE_PATH == NULL ||
123 _SM_STATE_PATH == NULL ||
124 _SM_DIR_PATH == NULL ||
125 _SM_BAK_PATH == NULL) {
126 nsm_log(LOG_WARNING, "unable to allocate memory");
129 strcat(strcpy(_SM_STATE_PATH, _SM_BASE_PATH), "/state");
130 strcat(strcpy(_SM_DIR_PATH, _SM_BASE_PATH), "/sm");
131 strcat(strcpy(_SM_BAK_PATH, _SM_BASE_PATH), "/sm.bak");
140 usage: fprintf(stderr,
141 "Usage: sm-notify [-dfq] [-m max-retry-minutes] [-p srcport]\n"
142 " [-P /path/to/state/directory] [-N my_host_name\n");
146 if (strcmp(_SM_BASE_PATH, BASEDIR) == 0) {
147 if (record_pid() == 0 && force == 0 && opt_update_state == 0)
148 /* already run, don't try again */
153 strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1);
155 if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) {
156 perror("gethostname");
160 backup_hosts(_SM_DIR_PATH, _SM_BAK_PATH);
161 get_hosts(_SM_BAK_PATH);
165 printf("Backgrounding to notify hosts...\n");
167 openlog("sm-notify", LOG_PID, LOG_DAEMON);
170 if (daemon(0, 0) < 0) {
171 nsm_log(LOG_WARNING, "unable to background: %s",
181 /* Get and update the NSM state. This will call sync() */
182 nsm_state = nsm_get_state(opt_update_state);
189 while ((hp = hosts) != 0) {
192 "Unable to notify %s, giving up",
207 nsm_address local_addr;
211 sock = socket(AF_INET, SOCK_DGRAM, 0);
216 fcntl(sock, F_SETFL, O_NONBLOCK);
218 memset(&local_addr, 0, sizeof(local_addr));
219 local_addr.ss_family = AF_INET; /* Default to IPv4 */
221 /* Bind source IP if provided on command line */
223 if (!host_lookup(AF_INET, opt_srcaddr, &local_addr)) {
225 "Not a valid hostname or address: \"%s\"\n",
229 /* We know it's IPv4 at this point */
232 /* Use source port if provided on the command line,
233 * otherwise use bindresvport */
235 addr_set_port(&local_addr, opt_srcport);
236 if (bind(sock, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0) {
241 (void) bindresvport(sock, (struct sockaddr_in *) &local_addr);
245 failtime = time(NULL) + opt_max_retry;
251 time_t now = time(NULL);
252 unsigned int sent = 0;
256 if (failtime && now >= failtime)
259 while ((wait = hosts->send_next - now) <= 0) {
260 /* Never send more than 10 packets at once */
264 /* Remove queue head */
268 notify_host(sock, hp);
270 /* Set the timeout for this call, using an
271 exponential timeout strategy */
273 if ((hp->timeout <<= 1) > NSM_MAX_TIMEOUT)
274 hp->timeout = NSM_MAX_TIMEOUT;
275 hp->send_next = now + wait;
281 nsm_log(LOG_DEBUG, "Host %s due in %ld seconds",
290 if (poll(&pfd, 1, wait) != 1)
298 * Send notification to a single host
301 notify_host(int sock, struct nsm_host *host)
303 static unsigned int xid = 0;
305 uint32_t msgbuf[MAXMSGSIZE], *p;
309 xid = getpid() + time(NULL);
313 memset(msgbuf, 0, sizeof(msgbuf));
315 *p++ = htonl(host->xid);
319 /* If we retransmitted 4 times, reset the port to force
320 * a new portmap lookup (in case statd was restarted)
322 if (host->retries >= 4) {
323 addr_set_port(&host->addr, 0);
328 if (addr_get_port(&dest) == 0) {
329 /* Build a PMAP packet */
330 nsm_log(LOG_DEBUG, "Sending portmap query to %s", host->name);
332 addr_set_port(&dest, 111);
333 *p++ = htonl(100000);
341 *p++ = htonl(NSM_PROGRAM);
342 *p++ = htonl(NSM_VERSION);
343 *p++ = htonl(IPPROTO_UDP);
346 /* Build an SM_NOTIFY packet */
347 nsm_log(LOG_DEBUG, "Sending SM_NOTIFY to %s", host->name);
349 *p++ = htonl(NSM_PROGRAM);
350 *p++ = htonl(NSM_VERSION);
351 *p++ = htonl(NSM_NOTIFY);
358 len = strlen(nsm_hostname);
360 memcpy(p, nsm_hostname, len);
362 *p++ = htonl(nsm_state);
364 len = (p - msgbuf) << 2;
366 sendto(sock, msgbuf, len, 0, (struct sockaddr *) &dest, sizeof(dest));
370 * Receive reply from remote host
376 uint32_t msgbuf[MAXMSGSIZE], *p, *end;
380 res = recv(sock, msgbuf, sizeof(msgbuf), 0);
384 nsm_log(LOG_DEBUG, "Received packet...");
387 end = p + (res >> 2);
390 if (*p++ != htonl(1) /* must be REPLY */
391 || *p++ != htonl(0) /* must be ACCEPTED */
392 || *p++ != htonl(0) /* must be NULL verifier */
394 || *p++ != htonl(0)) /* must be SUCCESS */
397 /* Before we look at the data, find the host struct for
399 if ((hp = find_host(xid)) == NULL)
402 if (addr_get_port(&hp->addr) == 0) {
403 /* This was a portmap request */
410 hp->send_next = time(NULL);
412 /* No binding for statd. Delay the next
413 * portmap query for max timeout */
414 nsm_log(LOG_DEBUG, "No statd on %s", hp->name);
415 hp->timeout = NSM_MAX_TIMEOUT;
416 hp->send_next += NSM_MAX_TIMEOUT;
418 addr_set_port(&hp->addr, port);
419 if (hp->timeout >= NSM_MAX_TIMEOUT / 4)
420 hp->timeout = NSM_MAX_TIMEOUT / 4;
424 /* Successful NOTIFY call. Server returns void,
425 * so nothing we need to do here (except
426 * check that we didn't read past the end of the
430 nsm_log(LOG_DEBUG, "Host %s notified successfully", hp->name);
439 fail: /* Re-insert the host */
444 * Back up all hosts from the sm directory to sm.bak
447 backup_hosts(const char *dirname, const char *bakname)
452 if (!(dir = opendir(dirname))) {
457 while ((de = readdir(dir)) != NULL) {
458 char src[1024], dst[1024];
460 if (de->d_name[0] == '.')
463 snprintf(src, sizeof(src), "%s/%s", dirname, de->d_name);
464 snprintf(dst, sizeof(dst), "%s/%s", bakname, de->d_name);
465 if (rename(src, dst) < 0) {
467 "Failed to rename %s -> %s: %m",
475 * Get all entries from sm.bak and convert them to host names
478 get_hosts(const char *dirname)
480 struct nsm_host *host;
484 if (!(dir = opendir(dirname))) {
490 while ((de = readdir(dir)) != NULL) {
494 if (de->d_name[0] == '.')
497 host = calloc(1, sizeof(*host));
499 snprintf(path, sizeof(path), "%s/%s", dirname, de->d_name);
500 if (!host_lookup(AF_UNSPEC, de->d_name, &host->addr)) {
502 "%s doesn't seem to be a valid address, skipped",
508 if (stat(path, &stb) < 0)
510 host->last_used = stb.st_mtime;
511 host->timeout = NSM_TIMEOUT;
512 host->path = strdup(path);
513 host->name = strdup(de->d_name);
525 * Insert host into sorted list
528 insert_host(struct nsm_host *host)
530 struct nsm_host **where, *p;
533 while ((p = *where) != 0) {
534 /* Sort in ascending order of timeout */
535 if (host->send_next < p->send_next)
537 /* If we have the same timeout, put the
538 * most recently used host first.
539 * This makes sure that "recent" hosts
540 * get notified first.
542 if (host->send_next == p->send_next
543 && host->last_used > p->last_used)
553 * Find host given the XID
556 find_host(uint32_t xid)
558 struct nsm_host **where, *p;
561 while ((p = *where) != 0) {
573 * Retrieve the current NSM state
576 nsm_get_state(int update)
578 char newfile[PATH_MAX];
581 if ((fd = open(_SM_STATE_PATH, O_RDONLY)) < 0) {
583 nsm_log(LOG_WARNING, "%s: %m", _SM_STATE_PATH);
584 nsm_log(LOG_WARNING, "Creating %s, set initial state 1",
590 if (read(fd, &state, sizeof(state)) != sizeof(state)) {
592 "%s: bad file size, setting state = 1",
605 snprintf(newfile, sizeof(newfile),
606 "%s.new", _SM_STATE_PATH);
607 if ((fd = open(newfile, O_CREAT|O_WRONLY, 0644)) < 0) {
608 nsm_log(LOG_WARNING, "Cannot create %s: %m", newfile);
611 if (write(fd, &state, sizeof(state)) != sizeof(state)) {
613 "Failed to write state to %s", newfile);
617 if (rename(newfile, _SM_STATE_PATH) < 0) {
619 "Cannot create %s: %m", _SM_STATE_PATH);
629 * Address handling utilities
633 addr_get_port(nsm_address *addr)
635 switch (((struct sockaddr *) addr)->sa_family) {
637 return ntohs(((struct sockaddr_in *) addr)->sin_port);
639 return ntohs(((struct sockaddr_in6 *) addr)->sin6_port);
645 addr_set_port(nsm_address *addr, int port)
647 switch (((struct sockaddr *) addr)->sa_family) {
649 ((struct sockaddr_in *) addr)->sin_port = htons(port);
652 ((struct sockaddr_in6 *) addr)->sin6_port = htons(port);
657 host_lookup(int af, const char *name, nsm_address *addr)
659 struct addrinfo hints, *ai;
662 memset(&hints, 0, sizeof(hints));
663 hints.ai_family = af;
665 if (getaddrinfo(name, NULL, &hints, &ai) != 0)
668 if (ai->ai_addrlen < sizeof(*addr)) {
669 memcpy(addr, ai->ai_addr, ai->ai_addrlen);
681 nsm_log(int fac, const char *fmt, ...)
685 if (fac == LOG_DEBUG && !opt_debug)
690 vsyslog(fac, fmt, ap);
692 vfprintf(stderr, fmt, ap);
699 * Record pid in /var/run/sm-notify.pid
700 * This file should remain until a reboot, even if the
702 * If file already exists, fail.
704 static int record_pid()
709 snprintf(pid, 20, "%d\n", getpid());
710 fd = open("/var/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600);
713 write(fd, pid, strlen(pid));
718 /* Drop privileges to match owner of state-directory
719 * (in case a reply triggers some unknown bug).
721 static void drop_privs(void)
725 if (stat(_SM_DIR_PATH, &st) == -1 &&
726 stat(_SM_BASE_PATH, &st) == -1) {
731 if (st.st_uid == 0) {
733 "sm-notify running as root. chown %s to choose different user\n",
739 if (setgid(st.st_gid) == -1
740 || setuid(st.st_uid) == -1) {
741 nsm_log(LOG_ERR, "Fail to drop privileges");