]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/statd/sm-notify.c
b7f43715b5e536d284571a7a506aaed9942b3a7d
[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 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10
11 #include <err.h>
12 #include <sys/types.h>
13 #include <sys/socket.h>
14 #include <sys/stat.h>
15 #include <sys/poll.h>
16 #include <sys/param.h>
17 #include <sys/syslog.h>
18 #include <arpa/inet.h>
19 #include <dirent.h>
20 #include <time.h>
21 #include <stdio.h>
22 #include <getopt.h>
23 #include <stdlib.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <netdb.h>
29 #include <errno.h>
30 #include <grp.h>
31
32 #include "sockaddr.h"
33 #include "xlog.h"
34 #include "nsm.h"
35 #include "nfsrpc.h"
36
37 #define NSM_TIMEOUT     2
38 #define NSM_MAX_TIMEOUT 120     /* don't make this too big */
39
40 struct nsm_host {
41         struct nsm_host *       next;
42         char *                  name;
43         char *                  mon_name;
44         char *                  my_name;
45         struct addrinfo         *ai;
46         time_t                  last_used;
47         time_t                  send_next;
48         unsigned int            timeout;
49         unsigned int            retries;
50         uint32_t                xid;
51 };
52
53 static char             nsm_hostname[SM_MAXSTRLEN + 1];
54 static int              nsm_state;
55 static int              nsm_family = AF_INET;
56 static int              opt_debug = 0;
57 static _Bool            opt_update_state = true;
58 static unsigned int     opt_max_retry = 15 * 60;
59 static char *           opt_srcaddr = NULL;
60 static char *           opt_srcport = NULL;
61
62 static void             notify(const int sock);
63 static int              notify_host(int, struct nsm_host *);
64 static void             recv_reply(int);
65 static void             insert_host(struct nsm_host *);
66 static struct nsm_host *find_host(uint32_t);
67 static int              record_pid(void);
68
69 static struct nsm_host *        hosts = NULL;
70
71 __attribute_malloc__
72 static struct addrinfo *
73 smn_lookup(const char *name)
74 {
75         struct addrinfo *ai = NULL;
76         struct addrinfo hint = {
77                 .ai_family      = (nsm_family == AF_INET ? AF_INET: AF_UNSPEC),
78                 .ai_protocol    = (int)IPPROTO_UDP,
79         };
80         int error;
81
82         error = getaddrinfo(name, NULL, &hint, &ai);
83         if (error != 0) {
84                 xlog(D_GENERAL, "getaddrinfo(3): %s", gai_strerror(error));
85                 return NULL;
86         }
87
88         return ai;
89 }
90
91 __attribute_malloc__
92 static struct nsm_host *
93 smn_alloc_host(const char *hostname, const char *mon_name,
94                 const char *my_name, const time_t timestamp)
95 {
96         struct nsm_host *host;
97
98         host = calloc(1, sizeof(*host));
99         if (host == NULL)
100                 goto out_nomem;
101
102         host->name = strdup(hostname);
103         host->mon_name = strdup(mon_name);
104         host->my_name = strdup(my_name);
105         if (host->name == NULL ||
106             host->mon_name == NULL ||
107             host->my_name == NULL) {
108                 free(host->my_name);
109                 free(host->mon_name);
110                 free(host->name);
111                 free(host);
112                 goto out_nomem;
113         }
114
115         host->last_used = timestamp;
116         host->timeout = NSM_TIMEOUT;
117         host->retries = 100;            /* force address retry */
118
119         return host;
120
121 out_nomem:
122         xlog_warn("Unable to allocate memory");
123         return NULL;
124 }
125
126 static void smn_forget_host(struct nsm_host *host)
127 {
128         xlog(D_CALL, "Removing %s (%s, %s) from notify list",
129                         host->name, host->mon_name, host->my_name);
130
131         nsm_delete_notified_host(host->name, host->mon_name, host->my_name);
132
133         free(host->my_name);
134         free(host->mon_name);
135         free(host->name);
136         if (host->ai)
137                 freeaddrinfo(host->ai);
138
139         free(host);
140 }
141
142 static unsigned int
143 smn_get_host(const char *hostname,
144                 __attribute__ ((unused)) const struct sockaddr *sap,
145                 const struct mon *m, const time_t timestamp)
146 {
147         struct nsm_host *host;
148
149         host = smn_alloc_host(hostname,
150                 m->mon_id.mon_name, m->mon_id.my_id.my_name, timestamp);
151         if (host == NULL)
152                 return 0;
153
154         insert_host(host);
155         xlog(D_GENERAL, "Added host %s to notify list", hostname);
156         return 1;
157 }
158
159 #ifdef IPV6_SUPPORTED
160 static int smn_socket(void)
161 {
162         int sock;
163
164         /*
165          * Use an AF_INET socket if IPv6 is disabled on the
166          * local system.
167          */
168         sock = socket(AF_INET6, SOCK_DGRAM, 0);
169         if (sock == -1) {
170                 if (errno != EAFNOSUPPORT) {
171                         xlog(L_ERROR, "Failed to create RPC socket: %m");
172                         return -1;
173                 }
174                 sock = socket(AF_INET, SOCK_DGRAM, 0);
175                 if (sock < 0) {
176                         xlog(L_ERROR, "Failed to create RPC socket: %m");
177                         return -1;
178                 }
179         } else
180                 nsm_family = AF_INET6;
181
182         if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
183                 xlog(L_ERROR, "fcntl(3) on RPC socket failed: %m");
184                 goto out_close;
185         }
186
187         /*
188          * TI-RPC over IPv6 (udp6/tcp6) does not handle IPv4.  However,
189          * since sm-notify open-codes all of its RPC support, it can
190          * use a single socket and let the local network stack provide
191          * the correct mapping between address families automatically.
192          * This is the same thing that is done in the kernel.
193          */
194         if (nsm_family == AF_INET6) {
195                 const int zero = 0;
196                 socklen_t zerolen = (socklen_t)sizeof(zero);
197
198                 if (setsockopt(sock, SOL_IPV6, IPV6_V6ONLY,
199                                         (char *)&zero, zerolen) == -1) {
200                         xlog(L_ERROR, "setsockopt(3) on RPC socket failed: %m");
201                         goto out_close;
202                 }
203         }
204
205         return sock;
206
207 out_close:
208         (void)close(sock);
209         return -1;
210 }
211 #else   /* !IPV6_SUPPORTED */
212 static int smn_socket(void)
213 {
214         int sock;
215
216         sock = socket(AF_INET, SOCK_DGRAM, 0);
217         if (sock == -1) {
218                 xlog(L_ERROR, "Failed to create RPC socket: %m");
219                 return -1;
220         }
221
222         if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
223                 xlog(L_ERROR, "fcntl(3) on RPC socket failed: %m");
224                 (void)close(sock);
225                 return -1;
226         }
227
228         return sock;
229 }
230 #endif  /* !IPV6_SUPPORTED */
231
232 /*
233  * If admin specified a source address or srcport, then convert those
234  * to a sockaddr and return it.   Otherwise, return an ANYADDR address.
235  */
236 __attribute_malloc__
237 static struct addrinfo *
238 smn_bind_address(const char *srcaddr, const char *srcport)
239 {
240         struct addrinfo *ai = NULL;
241         struct addrinfo hint = {
242                 .ai_flags       = AI_NUMERICSERV,
243                 .ai_family      = nsm_family,
244                 .ai_protocol    = (int)IPPROTO_UDP,
245         };
246         int error;
247
248         if (srcaddr == NULL)
249                 hint.ai_flags |= AI_PASSIVE;
250
251         if (srcport == NULL)
252                 error = getaddrinfo(srcaddr, "", &hint, &ai);
253         else
254                 error = getaddrinfo(srcaddr, srcport, &hint, &ai);
255         if (error != 0) {
256                 xlog(L_ERROR,
257                         "Invalid bind address or port for RPC socket: %s",
258                                 gai_strerror(error));
259                 return NULL;
260         }
261
262         return ai;
263 }
264
265 #ifdef HAVE_LIBTIRPC
266 static int
267 smn_bindresvport(int sock, struct sockaddr *sap)
268 {
269         return bindresvport_sa(sock, sap);
270 }
271
272 #else   /* !HAVE_LIBTIRPC */
273 static int
274 smn_bindresvport(int sock, struct sockaddr *sap)
275 {
276         if (sap->sa_family != AF_INET) {
277                 errno = EAFNOSUPPORT;
278                 return -1;
279         }
280
281         return bindresvport(sock, (struct sockaddr_in *)(char *)sap);
282 }
283 #endif  /* !HAVE_LIBTIRPC */
284
285 /*
286  * Prepare a socket for sending RPC requests
287  *
288  * Returns a bound datagram socket file descriptor, or -1 if
289  * an error occurs.
290  */
291 static int
292 smn_create_socket(const char *srcaddr, const char *srcport)
293 {
294         int sock, retry_cnt = 0;
295         struct addrinfo *ai;
296
297 retry:
298         sock = smn_socket();
299         if (sock == -1)
300                 return -1;
301
302         ai = smn_bind_address(srcaddr, srcport);
303         if (ai == NULL) {
304                 (void)close(sock);
305                 return -1;
306         }
307
308         /* Use source port if provided on the command line,
309          * otherwise use bindresvport */
310         if (srcport) {
311                 if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
312                         xlog(L_ERROR, "Failed to bind RPC socket: %m");
313                         freeaddrinfo(ai);
314                         (void)close(sock);
315                         return -1;
316                 }
317         } else {
318                 struct servent *se;
319
320                 if (smn_bindresvport(sock, ai->ai_addr) == -1) {
321                         xlog(L_ERROR,
322                                 "bindresvport on RPC socket failed: %m");
323                         freeaddrinfo(ai);
324                         (void)close(sock);
325                         return -1;
326                 }
327
328                 /* try to avoid known ports */
329                 se = getservbyport((int)nfs_get_port(ai->ai_addr), "udp");
330                 if (se != NULL && retry_cnt < 100) {
331                         retry_cnt++;
332                         freeaddrinfo(ai);
333                         (void)close(sock);
334                         goto retry;
335                 }
336         }
337
338         freeaddrinfo(ai);
339         return sock;
340 }
341
342 int
343 main(int argc, char **argv)
344 {
345         int     c, sock, force = 0;
346         char *  progname;
347
348         progname = strrchr(argv[0], '/');
349         if (progname != NULL)
350                 progname++;
351         else
352                 progname = argv[0];
353
354         while ((c = getopt(argc, argv, "dm:np:v:P:f")) != -1) {
355                 switch (c) {
356                 case 'f':
357                         force = 1;
358                         break;
359                 case 'd':
360                         opt_debug++;
361                         break;
362                 case 'm':
363                         opt_max_retry = atoi(optarg) * 60;
364                         break;
365                 case 'n':
366                         opt_update_state = false;
367                         break;
368                 case 'p':
369                         opt_srcport = optarg;
370                         break;
371                 case 'v':
372                         opt_srcaddr = optarg;
373                         break;
374                 case 'P':
375                         if (!nsm_setup_pathnames(argv[0], optarg))
376                                 exit(1);
377                         break;
378
379                 default:
380                         goto usage;
381                 }
382         }
383
384         if (optind < argc) {
385 usage:          fprintf(stderr,
386                         "Usage: %s -notify [-dfq] [-m max-retry-minutes] [-p srcport]\n"
387                         "            [-P /path/to/state/directory] [-v my_host_name]\n",
388                         progname);
389                 exit(1);
390         }
391
392         xlog_syslog(1);
393         if (opt_debug) {
394                 xlog_stderr(1);
395                 xlog_config(D_ALL, 1);
396         } else
397                 xlog_stderr(0);
398
399         xlog_open(progname);
400         xlog(L_NOTICE, "Version " VERSION " starting");
401
402         if (nsm_is_default_parentdir()) {
403                 if (record_pid() == 0 && force == 0 && opt_update_state) {
404                         /* already run, don't try again */
405                         xlog(L_NOTICE, "Already notifying clients; Exiting!");
406                         exit(0);
407                 }
408         }
409
410         if (opt_srcaddr != NULL) {
411                 struct addrinfo *ai = NULL;
412                 struct addrinfo hint = {
413                         .ai_family      = AF_UNSPEC,
414                         .ai_flags       = AI_NUMERICHOST,
415                 };
416
417                 if (getaddrinfo(opt_srcaddr, NULL, &hint, &ai))
418                         /* not a presentation address - use it */
419                         strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname));
420                 else {
421                         /* was a presentation address - look it up in
422                          * /etc/hosts, so it can be used for my_name */
423                         int error;
424
425                         freeaddrinfo(ai);
426                         hint.ai_flags = AI_CANONNAME;
427                         error = getaddrinfo(opt_srcaddr, NULL, &hint, &ai);
428                         if (error != 0) {
429                                 xlog(L_ERROR, "Bind address %s is unusable: %s",
430                                                 opt_srcaddr, gai_strerror(error));
431                                 exit(1);
432                         }
433                         strncpy(nsm_hostname, ai->ai_canonname,
434                                                         sizeof(nsm_hostname));
435                         freeaddrinfo(ai);
436                 }
437         }
438
439         (void)nsm_retire_monitored_hosts();
440         if (nsm_load_notify_list(smn_get_host) == 0) {
441                 xlog(D_GENERAL, "No hosts to notify; exiting");
442                 return 0;
443         }
444
445         nsm_state = nsm_get_state(opt_update_state);
446         if (nsm_state == 0)
447                 exit(1);
448         nsm_update_kernel_state(nsm_state);
449
450         if (!opt_debug) {
451                 xlog(L_NOTICE, "Backgrounding to notify hosts...\n");
452
453                 if (daemon(0, 0) < 0) {
454                         xlog(L_ERROR, "unable to background: %m");
455                         exit(1);
456                 }
457
458                 close(0);
459                 close(1);
460                 close(2);
461         }
462
463         sock = smn_create_socket(opt_srcaddr, opt_srcport);
464         if (sock == -1)
465                 exit(1);
466
467         if (!nsm_drop_privileges(-1))
468                 exit(1);
469
470         notify(sock);
471
472         if (hosts) {
473                 struct nsm_host *hp;
474
475                 while ((hp = hosts) != 0) {
476                         hosts = hp->next;
477                         xlog(L_NOTICE, "Unable to notify %s, giving up",
478                                 hp->name);
479                 }
480                 exit(1);
481         }
482
483         exit(0);
484 }
485
486 /*
487  * Notify hosts
488  */
489 static void
490 notify(const int sock)
491 {
492         time_t  failtime = 0;
493
494         if (opt_max_retry)
495                 failtime = time(NULL) + opt_max_retry;
496
497         while (hosts) {
498                 struct pollfd   pfd;
499                 time_t          now = time(NULL);
500                 unsigned int    sent = 0;
501                 struct nsm_host *hp;
502                 long            wait;
503
504                 if (failtime && now >= failtime)
505                         break;
506
507                 while (hosts && ((wait = hosts->send_next - now) <= 0)) {
508                         /* Never send more than 10 packets at once */
509                         if (sent++ >= 10)
510                                 break;
511
512                         /* Remove queue head */
513                         hp = hosts;
514                         hosts = hp->next;
515
516                         if (notify_host(sock, hp))
517                                 continue;
518
519                         /* Set the timeout for this call, using an
520                            exponential timeout strategy */
521                         wait = hp->timeout;
522                         if ((hp->timeout <<= 1) > NSM_MAX_TIMEOUT)
523                                 hp->timeout = NSM_MAX_TIMEOUT;
524                         hp->send_next = now + wait;
525                         hp->retries++;
526
527                         insert_host(hp);
528                 }
529                 if (hosts == NULL)
530                         return;
531
532                 xlog(D_GENERAL, "Host %s due in %ld seconds",
533                                 hosts->name, wait);
534
535                 pfd.fd = sock;
536                 pfd.events = POLLIN;
537
538                 wait *= 1000;
539                 if (wait < 100)
540                         wait = 100;
541                 if (poll(&pfd, 1, wait) != 1)
542                         continue;
543
544                 recv_reply(sock);
545         }
546 }
547
548 /*
549  * Send notification to a single host
550  */
551 static int
552 notify_host(int sock, struct nsm_host *host)
553 {
554         const char *my_name = (opt_srcaddr != NULL ?
555                                         nsm_hostname : host->my_name);
556         struct sockaddr *sap;
557         socklen_t salen;
558
559         if (host->ai == NULL) {
560                 host->ai = smn_lookup(host->name);
561                 if (host->ai == NULL) {
562                         xlog_warn("DNS resolution of %s failed; "
563                                 "retrying later", host->name);
564                         return 0;
565                 }
566         }
567
568         /* If we retransmitted 4 times, reset the port to force
569          * a new portmap lookup (in case statd was restarted).
570          * We also rotate through multiple IP addresses at this
571          * point.
572          */
573         if (host->retries >= 4) {
574                 /* don't rotate if there is only one addrinfo */
575                 if (host->ai->ai_next != NULL) {
576                         struct addrinfo *first = host->ai;
577                         struct addrinfo **next = &host->ai;
578
579                         /* remove the first entry from the list */
580                         host->ai = first->ai_next;
581                         first->ai_next = NULL;
582                         /* find the end of the list */
583                         next = &first->ai_next;
584                         while ( *next )
585                                 next = & (*next)->ai_next;
586                         /* put first entry at end */
587                         *next = first;
588                 }
589
590                 nfs_set_port(host->ai->ai_addr, 0);
591                 host->retries = 0;
592         }
593
594         sap = host->ai->ai_addr;
595         salen = host->ai->ai_addrlen;
596
597         if (nfs_get_port(sap) == 0)
598                 host->xid = nsm_xmit_rpcbind(sock, sap, SM_PROG, SM_VERS);
599         else
600                 host->xid = nsm_xmit_notify(sock, sap, salen,
601                                         SM_PROG, my_name, nsm_state);
602
603         return 0;
604 }
605
606 /*
607  * Extract the returned port number and set up the SM_NOTIFY call.
608  */
609 static void
610 recv_rpcbind_reply(struct sockaddr *sap, struct nsm_host *host, XDR *xdr)
611 {
612         uint16_t port = nsm_recv_rpcbind(sap->sa_family, xdr);
613
614         host->send_next = time(NULL);
615         host->xid = 0;
616
617         if (port == 0) {
618                 /* No binding for statd... */
619                 xlog(D_GENERAL, "No statd on host %s", host->name);
620                 host->timeout = NSM_MAX_TIMEOUT;
621                 host->send_next += NSM_MAX_TIMEOUT;
622         } else {
623                 nfs_set_port(sap, port);
624                 if (host->timeout >= NSM_MAX_TIMEOUT / 4)
625                         host->timeout = NSM_MAX_TIMEOUT / 4;
626         }
627
628         insert_host(host);
629 }
630
631 /*
632  * Successful NOTIFY call. Server returns void.
633  *
634  * Try sending another SM_NOTIFY with an unqualified "my_name"
635  * argument.  Reuse the port number.  If "my_name" is already
636  * unqualified, we're done.
637  */
638 static void
639 recv_notify_reply(struct nsm_host *host)
640 {
641         char *dot = strchr(host->my_name, '.');
642
643         if (dot != NULL) {
644                 *dot = '\0';
645                 host->send_next = time(NULL);
646                 host->xid = 0;
647                 if (host->timeout >= NSM_MAX_TIMEOUT / 4)
648                         host->timeout = NSM_MAX_TIMEOUT / 4;
649                 insert_host(host);
650         } else {
651                 xlog(D_GENERAL, "Host %s notified successfully", host->name);
652                 smn_forget_host(host);
653         }
654 }
655
656 /*
657  * Receive reply from remote host
658  */
659 static void
660 recv_reply(int sock)
661 {
662         struct nsm_host *hp;
663         struct sockaddr *sap;
664         char msgbuf[NSM_MAXMSGSIZE];
665         uint32_t        xid;
666         ssize_t         msglen;
667         XDR             xdr;
668
669         memset(msgbuf, 0 , sizeof(msgbuf));
670         msglen = recv(sock, msgbuf, sizeof(msgbuf), 0);
671         if (msglen < 0)
672                 return;
673
674         xlog(D_GENERAL, "Received packet...");
675
676         memset(&xdr, 0, sizeof(xdr));
677         xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE);
678         xid = nsm_parse_reply(&xdr);
679         if (xid == 0)
680                 goto out;
681
682         /* Before we look at the data, find the host struct for
683            this reply */
684         if ((hp = find_host(xid)) == NULL)
685                 goto out;
686
687         sap = hp->ai->ai_addr;
688         if (nfs_get_port(sap) == 0)
689                 recv_rpcbind_reply(sap, hp, &xdr);
690         else
691                 recv_notify_reply(hp);
692
693 out:
694         xdr_destroy(&xdr);
695 }
696
697 /*
698  * Insert host into sorted list
699  */
700 static void
701 insert_host(struct nsm_host *host)
702 {
703         struct nsm_host **where, *p;
704
705         where = &hosts;
706         while ((p = *where) != 0) {
707                 /* Sort in ascending order of timeout */
708                 if (host->send_next < p->send_next)
709                         break;
710                 /* If we have the same timeout, put the
711                  * most recently used host first.
712                  * This makes sure that "recent" hosts
713                  * get notified first.
714                  */
715                 if (host->send_next == p->send_next
716                  && host->last_used > p->last_used)
717                         break;
718                 where = &p->next;
719         }
720
721         host->next = *where;
722         *where = host;
723 }
724
725 /*
726  * Find host given the XID
727  */
728 static struct nsm_host *
729 find_host(uint32_t xid)
730 {
731         struct nsm_host **where, *p;
732
733         where = &hosts;
734         while ((p = *where) != 0) {
735                 if (p->xid == xid) {
736                         *where = p->next;
737                         return p;
738                 }
739                 where = &p->next;
740         }
741         return NULL;
742 }
743
744 /*
745  * Record pid in /var/run/sm-notify.pid
746  * This file should remain until a reboot, even if the
747  * program exits.
748  * If file already exists, fail.
749  */
750 static int record_pid(void)
751 {
752         char pid[20];
753         ssize_t len;
754         int fd;
755
756         (void)snprintf(pid, sizeof(pid), "%d\n", (int)getpid());
757         fd = open("/var/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600);
758         if (fd < 0)
759                 return 0;
760
761         len = write(fd, pid, strlen(pid));
762         if ((len < 0) || ((size_t)len != strlen(pid))) {
763                 xlog_warn("Writing to pid file failed: errno %d (%m)",
764                                 errno);
765         }
766
767         (void)close(fd);
768         return 1;
769 }