]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/statd/sm-notify.c
sm-notify: Support creating a PF_INET6 socket 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 = 0;
57 static uint16_t         opt_srcport = 0;
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  * Prepare a socket for sending RPC requests
220  *
221  * Returns a bound datagram socket file descriptor, or -1 if
222  * an error occurs.
223  */
224 static int
225 smn_create_socket(const char *srcaddr, const uint16_t srcport)
226 {
227         struct sockaddr_storage address;
228         struct sockaddr *local_addr = (struct sockaddr *)&address;
229         int sock, retry_cnt = 0;
230
231 retry:
232         sock = smn_socket();
233         if (sock == -1)
234                 return -1;
235
236         memset(&address, 0, sizeof(address));
237         local_addr->sa_family = AF_INET;        /* Default to IPv4 */
238
239         /* Bind source IP if provided on command line */
240         if (srcaddr) {
241                 struct addrinfo *ai = smn_lookup(srcaddr);
242                 if (!ai) {
243                         xlog(L_ERROR,
244                                 "Not a valid hostname or address: \"%s\"",
245                                 srcaddr);
246                         (void)close(sock);
247                         return -1;
248                 }
249
250                 /* We know it's IPv4 at this point */
251                 memcpy(local_addr, ai->ai_addr, ai->ai_addrlen);
252
253                 freeaddrinfo(ai);
254         }
255
256         /* Use source port if provided on the command line,
257          * otherwise use bindresvport */
258         if (srcport) {
259                 nfs_set_port(local_addr, srcport);
260                 if (bind(sock, local_addr, sizeof(struct sockaddr_in)) < 0) {
261                         xlog(L_ERROR, "Failed to bind RPC socket: %m");
262                         (void)close(sock);
263                         return -1;
264                 }
265         } else {
266                 struct servent *se;
267                 struct sockaddr_in *sin = (struct sockaddr_in *)local_addr;
268                 (void) bindresvport(sock, sin);
269                 /* try to avoid known ports */
270                 se = getservbyport(sin->sin_port, "udp");
271                 if (se && retry_cnt < 100) {
272                         retry_cnt++;
273                         close(sock);
274                         goto retry;
275                 }
276         }
277
278         return sock;
279 }
280
281 int
282 main(int argc, char **argv)
283 {
284         int     c, sock, force = 0;
285         char *  progname;
286
287         progname = strrchr(argv[0], '/');
288         if (progname != NULL)
289                 progname++;
290         else
291                 progname = argv[0];
292
293         while ((c = getopt(argc, argv, "dm:np:v:P:f")) != -1) {
294                 switch (c) {
295                 case 'f':
296                         force = 1;
297                         break;
298                 case 'd':
299                         opt_debug++;
300                         break;
301                 case 'm':
302                         opt_max_retry = atoi(optarg) * 60;
303                         break;
304                 case 'n':
305                         opt_update_state = false;
306                         break;
307                 case 'p':
308                         opt_srcport = atoi(optarg);
309                         break;
310                 case 'v':
311                         opt_srcaddr = optarg;
312                         break;
313                 case 'P':
314                         if (!nsm_setup_pathnames(argv[0], optarg))
315                                 exit(1);
316                         break;
317
318                 default:
319                         goto usage;
320                 }
321         }
322
323         if (optind < argc) {
324 usage:          fprintf(stderr,
325                         "Usage: %s -notify [-dfq] [-m max-retry-minutes] [-p srcport]\n"
326                         "            [-P /path/to/state/directory] [-v my_host_name]\n",
327                         progname);
328                 exit(1);
329         }
330
331         xlog_syslog(1);
332         if (opt_debug) {
333                 xlog_stderr(1);
334                 xlog_config(D_ALL, 1);
335         } else
336                 xlog_stderr(0);
337
338         xlog_open(progname);
339         xlog(L_NOTICE, "Version " VERSION " starting");
340
341         if (nsm_is_default_parentdir()) {
342                 if (record_pid() == 0 && force == 0 && opt_update_state) {
343                         /* already run, don't try again */
344                         xlog(L_NOTICE, "Already notifying clients; Exiting!");
345                         exit(0);
346                 }
347         }
348
349         if (opt_srcaddr) {
350                 strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1);
351         } else
352         if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) {
353                 xlog(L_ERROR, "Failed to obtain name of local host: %m");
354                 exit(1);
355         }
356
357         (void)nsm_retire_monitored_hosts();
358         if (nsm_load_notify_list(smn_get_host) == 0) {
359                 xlog(D_GENERAL, "No hosts to notify; exiting");
360                 return 0;
361         }
362
363         nsm_state = nsm_get_state(opt_update_state);
364         if (nsm_state == 0)
365                 exit(1);
366         nsm_update_kernel_state(nsm_state);
367
368         if (!opt_debug) {
369                 xlog(L_NOTICE, "Backgrounding to notify hosts...\n");
370
371                 if (daemon(0, 0) < 0) {
372                         xlog(L_ERROR, "unable to background: %m");
373                         exit(1);
374                 }
375
376                 close(0);
377                 close(1);
378                 close(2);
379         }
380
381         sock = smn_create_socket(opt_srcaddr, opt_srcport);
382         if (sock == -1)
383                 exit(1);
384
385         if (!nsm_drop_privileges(-1))
386                 exit(1);
387
388         notify(sock);
389
390         if (hosts) {
391                 struct nsm_host *hp;
392
393                 while ((hp = hosts) != 0) {
394                         hosts = hp->next;
395                         xlog(L_NOTICE, "Unable to notify %s, giving up",
396                                 hp->name);
397                 }
398                 exit(1);
399         }
400
401         exit(0);
402 }
403
404 /*
405  * Notify hosts
406  */
407 static void
408 notify(const int sock)
409 {
410         time_t  failtime = 0;
411
412         if (opt_max_retry)
413                 failtime = time(NULL) + opt_max_retry;
414
415         while (hosts) {
416                 struct pollfd   pfd;
417                 time_t          now = time(NULL);
418                 unsigned int    sent = 0;
419                 struct nsm_host *hp;
420                 long            wait;
421
422                 if (failtime && now >= failtime)
423                         break;
424
425                 while (hosts && ((wait = hosts->send_next - now) <= 0)) {
426                         /* Never send more than 10 packets at once */
427                         if (sent++ >= 10)
428                                 break;
429
430                         /* Remove queue head */
431                         hp = hosts;
432                         hosts = hp->next;
433
434                         if (notify_host(sock, hp))
435                                 continue;
436
437                         /* Set the timeout for this call, using an
438                            exponential timeout strategy */
439                         wait = hp->timeout;
440                         if ((hp->timeout <<= 1) > NSM_MAX_TIMEOUT)
441                                 hp->timeout = NSM_MAX_TIMEOUT;
442                         hp->send_next = now + wait;
443                         hp->retries++;
444
445                         insert_host(hp);
446                 }
447                 if (hosts == NULL)
448                         return;
449
450                 xlog(D_GENERAL, "Host %s due in %ld seconds",
451                                 hosts->name, wait);
452
453                 pfd.fd = sock;
454                 pfd.events = POLLIN;
455
456                 wait *= 1000;
457                 if (wait < 100)
458                         wait = 100;
459                 if (poll(&pfd, 1, wait) != 1)
460                         continue;
461
462                 recv_reply(sock);
463         }
464 }
465
466 /*
467  * Send notification to a single host
468  */
469 static int
470 notify_host(int sock, struct nsm_host *host)
471 {
472         struct sockaddr *sap;
473         socklen_t salen;
474
475         if (host->ai == NULL) {
476                 host->ai = smn_lookup(host->name);
477                 if (host->ai == NULL) {
478                         xlog_warn("DNS resolution of %s failed; "
479                                 "retrying later", host->name);
480                         return 0;
481                 }
482         }
483
484         /* If we retransmitted 4 times, reset the port to force
485          * a new portmap lookup (in case statd was restarted).
486          * We also rotate through multiple IP addresses at this
487          * point.
488          */
489         if (host->retries >= 4) {
490                 /* don't rotate if there is only one addrinfo */
491                 if (host->ai->ai_next != NULL) {
492                         struct addrinfo *first = host->ai;
493                         struct addrinfo **next = &host->ai;
494
495                         /* remove the first entry from the list */
496                         host->ai = first->ai_next;
497                         first->ai_next = NULL;
498                         /* find the end of the list */
499                         next = &first->ai_next;
500                         while ( *next )
501                                 next = & (*next)->ai_next;
502                         /* put first entry at end */
503                         *next = first;
504                 }
505
506                 nfs_set_port(host->ai->ai_addr, 0);
507                 host->retries = 0;
508         }
509
510         sap = host->ai->ai_addr;
511         salen = host->ai->ai_addrlen;
512
513         if (nfs_get_port(sap) == 0)
514                 host->xid = nsm_xmit_rpcbind(sock, sap, SM_PROG, SM_VERS);
515         else
516                 host->xid = nsm_xmit_notify(sock, sap, salen,
517                                 SM_PROG, nsm_hostname, nsm_state);
518         
519         return 0;
520 }
521
522 /*
523  * Extract the returned port number and set up the SM_NOTIFY call.
524  */
525 static void
526 recv_rpcbind_reply(struct sockaddr *sap, struct nsm_host *host, XDR *xdr)
527 {
528         uint16_t port = nsm_recv_rpcbind(sap->sa_family, xdr);
529
530         host->send_next = time(NULL);
531         host->xid = 0;
532
533         if (port == 0) {
534                 /* No binding for statd... */
535                 xlog(D_GENERAL, "No statd on host %s", host->name);
536                 host->timeout = NSM_MAX_TIMEOUT;
537                 host->send_next += NSM_MAX_TIMEOUT;
538         } else {
539                 nfs_set_port(sap, port);
540                 if (host->timeout >= NSM_MAX_TIMEOUT / 4)
541                         host->timeout = NSM_MAX_TIMEOUT / 4;
542         }
543
544         insert_host(host);
545 }
546
547 /*
548  * Successful NOTIFY call. Server returns void, so nothing
549  * we need to do here.
550  */
551 static void
552 recv_notify_reply(struct nsm_host *host)
553 {
554         xlog(D_GENERAL, "Host %s notified successfully", host->name);
555
556         smn_forget_host(host);
557 }
558
559 /*
560  * Receive reply from remote host
561  */
562 static void
563 recv_reply(int sock)
564 {
565         struct nsm_host *hp;
566         struct sockaddr *sap;
567         char msgbuf[NSM_MAXMSGSIZE];
568         uint32_t        xid;
569         ssize_t         msglen;
570         XDR             xdr;
571
572         memset(msgbuf, 0 , sizeof(msgbuf));
573         msglen = recv(sock, msgbuf, sizeof(msgbuf), 0);
574         if (msglen < 0)
575                 return;
576
577         xlog(D_GENERAL, "Received packet...");
578
579         memset(&xdr, 0, sizeof(xdr));
580         xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE);
581         xid = nsm_parse_reply(&xdr);
582         if (xid == 0)
583                 goto out;
584
585         /* Before we look at the data, find the host struct for
586            this reply */
587         if ((hp = find_host(xid)) == NULL)
588                 goto out;
589
590         sap = hp->ai->ai_addr;
591         if (nfs_get_port(sap) == 0)
592                 recv_rpcbind_reply(sap, hp, &xdr);
593         else
594                 recv_notify_reply(hp);
595
596 out:
597         xdr_destroy(&xdr);
598 }
599
600 /*
601  * Insert host into sorted list
602  */
603 static void
604 insert_host(struct nsm_host *host)
605 {
606         struct nsm_host **where, *p;
607
608         where = &hosts;
609         while ((p = *where) != 0) {
610                 /* Sort in ascending order of timeout */
611                 if (host->send_next < p->send_next)
612                         break;
613                 /* If we have the same timeout, put the
614                  * most recently used host first.
615                  * This makes sure that "recent" hosts
616                  * get notified first.
617                  */
618                 if (host->send_next == p->send_next
619                  && host->last_used > p->last_used)
620                         break;
621                 where = &p->next;
622         }
623
624         host->next = *where;
625         *where = host;
626 }
627
628 /*
629  * Find host given the XID
630  */
631 static struct nsm_host *
632 find_host(uint32_t xid)
633 {
634         struct nsm_host **where, *p;
635
636         where = &hosts;
637         while ((p = *where) != 0) {
638                 if (p->xid == xid) {
639                         *where = p->next;
640                         return p;
641                 }
642                 where = &p->next;
643         }
644         return NULL;
645 }
646
647 /*
648  * Record pid in /var/run/sm-notify.pid
649  * This file should remain until a reboot, even if the
650  * program exits.
651  * If file already exists, fail.
652  */
653 static int record_pid(void)
654 {
655         char pid[20];
656         ssize_t len;
657         int fd;
658
659         (void)snprintf(pid, sizeof(pid), "%d\n", (int)getpid());
660         fd = open("/var/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600);
661         if (fd < 0)
662                 return 0;
663
664         len = write(fd, pid, strlen(pid));
665         if ((len < 0) || ((size_t)len != strlen(pid))) {
666                 xlog_warn("Writing to pid file failed: errno %d (%m)",
667                                 errno);
668         }
669
670         (void)close(fd);
671         return 1;
672 }