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