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