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