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