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