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