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