sm-notify: Allow base path to be set by command line option.
[nfs-utils.git] / utils / statd / sm-notify.c
1 /*
2  * Send NSM notify calls to all hosts listed in /var/lib/sm
3  *
4  * Copyright (C) 2004-2006 Olaf Kirch <okir@suse.de>
5  */
6
7 #include <sys/types.h>
8 #include <sys/socket.h>
9 #include <sys/stat.h>
10 #include <sys/poll.h>
11 #include <sys/param.h>
12 #include <sys/syslog.h>
13 #include <arpa/inet.h>
14 #include <dirent.h>
15 #include <time.h>
16 #include <stdio.h>
17 #include <getopt.h>
18 #include <stdlib.h>
19 #include <fcntl.h>
20 #include <unistd.h>
21 #include <string.h>
22 #include <stdarg.h>
23 #include <netdb.h>
24 #include <errno.h>
25
26 #ifndef BASEDIR
27 #define BASEDIR         "/var/lib/nfs"
28 #endif
29
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"
33
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;
38
39 #define NSM_PROG        100024
40 #define NSM_PROGRAM     100024
41 #define NSM_VERSION     1
42 #define NSM_TIMEOUT     2
43 #define NSM_NOTIFY      6
44 #define NSM_MAX_TIMEOUT 120     /* don't make this too big */
45 #define MAXMSGSIZE      256
46
47 typedef struct sockaddr_storage nsm_address;
48
49 struct nsm_host {
50         struct nsm_host *       next;
51         char *                  name;
52         char *                  path;
53         nsm_address             addr;
54         time_t                  last_used;
55         time_t                  send_next;
56         unsigned int            timeout;
57         unsigned int            retries;
58         unsigned int            xid;
59 };
60
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;
70
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_parse(int, const char *, nsm_address *);
80 static int              addr_get_port(nsm_address *);
81 static void             addr_set_port(nsm_address *, int);
82 static int              host_lookup(int, const char *, nsm_address *);
83 void                    nsm_log(int fac, const char *fmt, ...);
84
85 static struct nsm_host *        hosts = NULL;
86
87 int
88 main(int argc, char **argv)
89 {
90         int     c;
91
92         while ((c = getopt(argc, argv, "dm:np:v:qP:")) != -1) {
93                 switch (c) {
94                 case 'd':
95                         opt_debug++;
96                         break;
97                 case 'm':
98                         opt_max_retry = atoi(optarg) * 60;
99                         break;
100                 case 'n':
101                         opt_update_state = 0;
102                         break;
103                 case 'p':
104                         opt_srcport = atoi(optarg);
105                         break;
106                 case 'v':
107                         opt_srcaddr = optarg;
108                         break;
109                 case 'q':
110                         opt_quiet = 1;
111                         break;
112                 case 'P':
113                         _SM_BASE_PATH = strdup(optarg);
114                         _SM_STATE_PATH = malloc(strlen(optarg)+1+sizeof("state"));
115                         _SM_DIR_PATH = malloc(strlen(optarg)+1+sizeof("sm"));
116                         _SM_BAK_PATH = malloc(strlen(optarg)+1+sizeof("sm.bak"));
117                         if (_SM_BASE_PATH == NULL ||
118                             _SM_STATE_PATH == NULL ||
119                             _SM_DIR_PATH == NULL ||
120                             _SM_BAK_PATH == NULL) {
121                                 nsm_log(LOG_WARNING, "unable to allocate memory");
122                                 exit(1);
123                         }
124                         strcat(strcpy(_SM_STATE_PATH, _SM_BASE_PATH), "/state");
125                         strcat(strcpy(_SM_DIR_PATH, _SM_BASE_PATH), "/sm");
126                         strcat(strcpy(_SM_BAK_PATH, _SM_BASE_PATH), "/sm.bak");
127                         break;
128
129                 default:
130                         goto usage;
131                 }
132         }
133
134         if (optind < argc) {
135 usage:          fprintf(stderr, "sm-notify [-d]\n");
136                 return 1;
137         }
138
139         if (opt_srcaddr) {
140                 strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1);
141         } else
142         if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) {
143                 perror("gethostname");
144                 return 1;
145         }
146
147         backup_hosts(_SM_DIR_PATH, _SM_BAK_PATH);
148         get_hosts(_SM_BAK_PATH);
149
150         if (!opt_debug) {
151                 if (!opt_quiet)
152                         printf("Backgrounding to notify hosts...\n");
153
154                 openlog("sm-notify", LOG_PID, LOG_DAEMON);
155                 log_syslog = 1;
156
157                 if (daemon(0, 0) < 0) {
158                         nsm_log(LOG_WARNING, "unable to background: %s",
159                                         strerror(errno));
160                         return 1;
161                 }
162
163                 close(0);
164                 close(1);
165                 close(2);
166         }
167
168         /* Get and update the NSM state. This will call sync() */
169         nsm_state = nsm_get_state(opt_update_state);
170
171         notify();
172
173         if (hosts) {
174                 struct nsm_host *hp;
175
176                 while ((hp = hosts) != 0) {
177                         hosts = hp->next;
178                         nsm_log(LOG_NOTICE,
179                                 "Unable to notify %s, giving up",
180                                 hp->name);
181                 }
182                 return 1;
183         }
184
185         return 0;
186 }
187
188 /*
189  * Notify hosts
190  */
191 void
192 notify(void)
193 {
194         nsm_address local_addr;
195         time_t  failtime = 0;
196         int     sock = -1;
197
198         sock = socket(AF_INET, SOCK_DGRAM, 0);
199         if (sock < 0) {
200                 perror("socket");
201                 exit(1);
202         }
203         fcntl(sock, F_SETFL, O_NONBLOCK);
204
205         memset(&local_addr, 0, sizeof(local_addr));
206         local_addr.ss_family = AF_INET; /* Default to IPv4 */
207
208         /* Bind source IP if provided on command line */
209         if (opt_srcaddr) {
210                 if (!addr_parse(AF_INET, opt_srcaddr, &local_addr)
211                  && !host_lookup(AF_INET, opt_srcaddr, &local_addr)) {
212                         nsm_log(LOG_WARNING,
213                                 "Not a valid hostname or address: \"%s\"\n",
214                                 opt_srcaddr);
215                         exit(1);
216                 }
217                 /* We know it's IPv4 at this point */
218         }
219
220         /* Use source port if provided on the command line,
221          * otherwise use bindresvport */
222         if (opt_srcport) {
223                 addr_set_port(&local_addr, opt_srcport);
224                 if (bind(sock, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0) {
225                         perror("bind");
226                         exit(1);
227                 }
228         } else {
229                 (void) bindresvport(sock, (struct sockaddr_in *) &local_addr);
230         }
231
232         if (opt_max_retry)
233                 failtime = time(NULL) + opt_max_retry;
234
235         while (hosts) {
236                 struct pollfd   pfd;
237                 time_t          now = time(NULL);
238                 unsigned int    sent = 0;
239                 struct nsm_host *hp;
240                 long            wait;
241
242                 if (failtime && now >= failtime)
243                         break;
244
245                 while ((wait = hosts->send_next - now) <= 0) {
246                         /* Never send more than 10 packets at once */
247                         if (sent++ >= 10)
248                                 break;
249
250                         /* Remove queue head */
251                         hp = hosts;
252                         hosts = hp->next;
253
254                         notify_host(sock, hp);
255
256                         /* Set the timeout for this call, using an
257                            exponential timeout strategy */
258                         wait = hp->timeout;
259                         if ((hp->timeout <<= 1) > NSM_MAX_TIMEOUT)
260                                 hp->timeout = NSM_MAX_TIMEOUT;
261                         hp->send_next = now + wait;
262                         hp->retries++;
263
264                         insert_host(hp);
265                 }
266
267                 nsm_log(LOG_DEBUG, "Host %s due in %ld seconds",
268                                 hosts->name, wait);
269
270                 pfd.fd = sock;
271                 pfd.events = POLLIN;
272
273                 wait *= 1000;
274                 if (wait < 100)
275                         wait = 100;
276                 if (poll(&pfd, 1, wait) != 1)
277                         continue;
278
279                 recv_reply(sock);
280         }
281 }
282
283 /*
284  * Send notification to a single host
285  */
286 void
287 notify_host(int sock, struct nsm_host *host)
288 {
289         static unsigned int     xid = 0;
290         nsm_address             dest;
291         uint32_t                msgbuf[MAXMSGSIZE], *p;
292         unsigned int            len;
293
294         if (!xid)
295                 xid = getpid() + time(NULL);
296         if (!host->xid)
297                 host->xid = xid++;
298
299         memset(msgbuf, 0, sizeof(msgbuf));
300         p = msgbuf;
301         *p++ = htonl(host->xid);
302         *p++ = 0;
303         *p++ = htonl(2);
304
305         /* If we retransmitted 4 times, reset the port to force
306          * a new portmap lookup (in case statd was restarted)
307          */
308         if (host->retries >= 4) {
309                 addr_set_port(&host->addr, 0);
310                 host->retries = 0;
311         }
312
313         dest = host->addr;
314         if (addr_get_port(&dest) == 0) {
315                 /* Build a PMAP packet */
316                 nsm_log(LOG_DEBUG, "Sending portmap query to %s", host->name);
317
318                 addr_set_port(&dest, 111);
319                 *p++ = htonl(100000);
320                 *p++ = htonl(2);
321                 *p++ = htonl(3);
322
323                 /* Auth and verf */
324                 *p++ = 0; *p++ = 0;
325                 *p++ = 0; *p++ = 0;
326
327                 *p++ = htonl(NSM_PROGRAM);
328                 *p++ = htonl(NSM_VERSION);
329                 *p++ = htonl(IPPROTO_UDP);
330                 *p++ = 0;
331         } else {
332                 /* Build an SM_NOTIFY packet */
333                 nsm_log(LOG_DEBUG, "Sending SM_NOTIFY to %s", host->name);
334
335                 *p++ = htonl(NSM_PROGRAM);
336                 *p++ = htonl(NSM_VERSION);
337                 *p++ = htonl(NSM_NOTIFY);
338
339                 /* Auth and verf */
340                 *p++ = 0; *p++ = 0;
341                 *p++ = 0; *p++ = 0;
342
343                 /* state change */
344                 len = strlen(nsm_hostname);
345                 *p++ = htonl(len);
346                 memcpy(p, nsm_hostname, len);
347                 p += (len + 3) >> 2;
348                 *p++ = htonl(nsm_state);
349         }
350         len = (p - msgbuf) << 2;
351
352         sendto(sock, msgbuf, len, 0, (struct sockaddr *) &dest, sizeof(dest));
353 }
354
355 /*
356  * Receive reply from remote host
357  */
358 void
359 recv_reply(int sock)
360 {
361         struct nsm_host *hp;
362         uint32_t        msgbuf[MAXMSGSIZE], *p, *end;
363         uint32_t        xid;
364         int             res;
365
366         res = recv(sock, msgbuf, sizeof(msgbuf), 0);
367         if (res < 0)
368                 return;
369
370         nsm_log(LOG_DEBUG, "Received packet...");
371
372         p = msgbuf;
373         end = p + (res >> 2);
374
375         xid = ntohl(*p++);
376         if (*p++ != htonl(1)    /* must be REPLY */
377          || *p++ != htonl(0)    /* must be ACCEPTED */
378          || *p++ != htonl(0)    /* must be NULL verifier */
379          || *p++ != htonl(0)
380          || *p++ != htonl(0))   /* must be SUCCESS */
381                 return;
382
383         /* Before we look at the data, find the host struct for
384            this reply */
385         if ((hp = find_host(xid)) == NULL)
386                 return;
387
388         if (addr_get_port(&hp->addr) == 0) {
389                 /* This was a portmap request */
390                 unsigned int    port;
391
392                 port = ntohl(*p++);
393                 if (p > end)
394                         goto fail;
395
396                 hp->send_next = time(NULL);
397                 if (port == 0) {
398                         /* No binding for statd. Delay the next
399                          * portmap query for max timeout */
400                         nsm_log(LOG_DEBUG, "No statd on %s", hp->name);
401                         hp->timeout = NSM_MAX_TIMEOUT;
402                         hp->send_next += NSM_MAX_TIMEOUT;
403                 } else {
404                         addr_set_port(&hp->addr, port);
405                         if (hp->timeout >= NSM_MAX_TIMEOUT / 4)
406                                 hp->timeout = NSM_MAX_TIMEOUT / 4;
407                 }
408                 hp->xid = 0;
409         } else {
410                 /* Successful NOTIFY call. Server returns void,
411                  * so nothing we need to do here (except
412                  * check that we didn't read past the end of the
413                  * packet)
414                  */
415                 if (p <= end) {
416                         nsm_log(LOG_DEBUG, "Host %s notified successfully", hp->name);
417                         unlink(hp->path);
418                         free(hp->name);
419                         free(hp->path);
420                         free(hp);
421                         return;
422                 }
423         }
424
425 fail:   /* Re-insert the host */
426         insert_host(hp);
427 }
428
429 /*
430  * Back up all hosts from the sm directory to sm.bak
431  */
432 static void
433 backup_hosts(const char *dirname, const char *bakname)
434 {
435         struct dirent   *de;
436         DIR             *dir;
437
438         if (!(dir = opendir(dirname))) {
439                 perror(dirname);
440                 return;
441         }
442
443         while ((de = readdir(dir)) != NULL) {
444                 char    src[1024], dst[1024];
445
446                 if (de->d_name[0] == '.')
447                         continue;
448
449                 snprintf(src, sizeof(src), "%s/%s", dirname, de->d_name);
450                 snprintf(dst, sizeof(dst), "%s/%s", bakname, de->d_name);
451                 if (rename(src, dst) < 0) {
452                         nsm_log(LOG_WARNING,
453                                 "Failed to rename %s -> %s: %m",
454                                 src, dst);
455                 }
456         }
457         closedir(dir);
458 }
459
460 /*
461  * Get all entries from sm.bak and convert them to host names
462  */
463 static void
464 get_hosts(const char *dirname)
465 {
466         struct nsm_host *host;
467         struct dirent   *de;
468         DIR             *dir;
469
470         if (!(dir = opendir(dirname))) {
471                 perror(dirname);
472                 return;
473         }
474
475         host = NULL;
476         while ((de = readdir(dir)) != NULL) {
477                 struct stat     stb;
478                 char            path[1024];
479
480                 if (de->d_name[0] == '.')
481                         continue;
482                 if (host == NULL)
483                         host = calloc(1, sizeof(*host));
484
485                 snprintf(path, sizeof(path), "%s/%s", dirname, de->d_name);
486                 if (!addr_parse(AF_INET, de->d_name, &host->addr)
487                  && !addr_parse(AF_INET6, de->d_name, &host->addr)
488                  && !host_lookup(AF_INET, de->d_name, &host->addr)) {
489                         nsm_log(LOG_WARNING,
490                                 "%s doesn't seem to be a valid address, skipped",
491                                 de->d_name);
492                         unlink(path);
493                         continue;
494                 }
495
496                 if (stat(path, &stb) < 0)
497                         continue;
498                 host->last_used = stb.st_mtime;
499                 host->timeout = NSM_TIMEOUT;
500                 host->path = strdup(path);
501                 host->name = strdup(de->d_name);
502
503                 insert_host(host);
504                 host = NULL;
505         }
506         closedir(dir);
507
508         if (host)
509                 free(host);
510 }
511
512 /*
513  * Insert host into sorted list
514  */
515 void
516 insert_host(struct nsm_host *host)
517 {
518         struct nsm_host **where, *p;
519
520         where = &hosts;
521         while ((p = *where) != 0) {
522                 /* Sort in ascending order of timeout */
523                 if (host->send_next < p->send_next)
524                         break;
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.
529                  */
530                 if (host->send_next == p->send_next
531                  && host->last_used > p->last_used)
532                         break;
533                 where = &p->next;
534         }
535
536         host->next = *where;
537         *where = host;
538 }
539
540 /*
541  * Find host given the XID
542  */
543 struct nsm_host *
544 find_host(uint32_t xid)
545 {
546         struct nsm_host **where, *p;
547
548         where = &hosts;
549         while ((p = *where) != 0) {
550                 if (p->xid == xid) {
551                         *where = p->next;
552                         return p;
553                 }
554                 where = &p->next;
555         }
556         return NULL;
557 }
558
559
560 /*
561  * Retrieve the current NSM state
562  */
563 unsigned int
564 nsm_get_state(int update)
565 {
566         char            newfile[PATH_MAX];
567         int             fd, state;
568
569         if ((fd = open(_SM_STATE_PATH, O_RDONLY)) < 0) {
570                 if (!opt_quiet) {
571                         nsm_log(LOG_WARNING, "%s: %m", _SM_STATE_PATH);
572                         nsm_log(LOG_WARNING, "Creating %s, set initial state 1",
573                                 _SM_STATE_PATH);
574                 }
575                 state = 1;
576                 update = 1;
577         } else {
578                 if (read(fd, &state, sizeof(state)) != sizeof(state)) {
579                         nsm_log(LOG_WARNING,
580                                 "%s: bad file size, setting state = 1",
581                                 _SM_STATE_PATH);
582                         state = 1;
583                         update = 1;
584                 } else {
585                         if (!(state & 1))
586                                 state += 1;
587                 }
588                 close(fd);
589         }
590
591         if (update) {
592                 state += 2;
593                 snprintf(newfile, sizeof(newfile),
594                                 "%s.new", _SM_STATE_PATH);
595                 if ((fd = open(newfile, O_CREAT|O_WRONLY, 0644)) < 0) {
596                         nsm_log(LOG_WARNING, "Cannot create %s: %m", newfile);
597                         exit(1);
598                 }
599                 if (write(fd, &state, sizeof(state)) != sizeof(state)) {
600                         nsm_log(LOG_WARNING,
601                                 "Failed to write state to %s", newfile);
602                         exit(1);
603                 }
604                 close(fd);
605                 if (rename(newfile, _SM_STATE_PATH) < 0) {
606                         nsm_log(LOG_WARNING,
607                                 "Cannot create %s: %m", _SM_STATE_PATH);
608                         exit(1);
609                 }
610                 sync();
611         }
612
613         return state;
614 }
615
616 /*
617  * Address handling utilities
618  */
619 static int
620 addr_parse(int af, const char *name, nsm_address *addr)
621 {
622         void    *ptr;
623
624         if (af == AF_INET)
625                 ptr = &((struct sockaddr_in *) addr)->sin_addr;
626         else if (af == AF_INET6)
627                 ptr = &((struct sockaddr_in6 *) addr)->sin6_addr;
628         else
629                 return 0;
630         if (inet_pton(af, name, ptr) <= 0)
631                 return 0;
632         ((struct sockaddr *) addr)->sa_family = af;
633         return 1;
634 }
635
636 int
637 addr_get_port(nsm_address *addr)
638 {
639         switch (((struct sockaddr *) addr)->sa_family) {
640         case AF_INET:
641                 return ntohs(((struct sockaddr_in *) addr)->sin_port);
642         case AF_INET6:
643                 return ntohs(((struct sockaddr_in6 *) addr)->sin6_port);
644         }
645         return 0;
646 }
647
648 static void
649 addr_set_port(nsm_address *addr, int port)
650 {
651         switch (((struct sockaddr *) addr)->sa_family) {
652         case AF_INET:
653                 ((struct sockaddr_in *) addr)->sin_port = htons(port);
654                 break;
655         case AF_INET6:
656                 ((struct sockaddr_in6 *) addr)->sin6_port = htons(port);
657         }
658 }
659
660 static int
661 host_lookup(int af, const char *name, nsm_address *addr)
662 {
663         struct addrinfo hints, *ai;
664         int okay = 0;
665
666         memset(&hints, 0, sizeof(hints));
667         hints.ai_family = af;
668
669         if (getaddrinfo(name, NULL, &hints, &ai) != 0)
670                 return 0;
671
672         if (ai->ai_addrlen < sizeof(*addr)) {
673                 memcpy(addr, ai->ai_addr, ai->ai_addrlen);
674                 okay = 1;
675         }
676
677         freeaddrinfo(ai);
678         return okay;
679 }
680
681 /*
682  * Log a message
683  */
684 void
685 nsm_log(int fac, const char *fmt, ...)
686 {
687         va_list ap;
688
689         if (fac == LOG_DEBUG && !opt_debug)
690                 return;
691
692         va_start(ap, fmt);
693         if (log_syslog)
694                 vsyslog(fac, fmt, ap);
695         else {
696                 vfprintf(stderr, fmt, ap);
697                 fputs("\n", stderr);
698         }
699         va_end(ap);
700 }