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