Correct PPTP server firewall rules chain.
[tomato/davidwu.git] / release / src / router / nfs-utils / utils / statd / sm-notify.c
blobd58e0be421859ed6c59880e917d53a53a874b877
1 /*
2 * Send NSM notify calls to all hosts listed in /var/lib/sm
4 * Copyright (C) 2004-2006 Olaf Kirch <okir@suse.de>
5 */
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
11 #include <sys/types.h>
12 #include <sys/socket.h>
13 #include <sys/stat.h>
14 #include <sys/poll.h>
15 #include <sys/param.h>
16 #include <sys/syslog.h>
17 #include <arpa/inet.h>
18 #include <dirent.h>
19 #include <time.h>
20 #include <stdio.h>
21 #include <getopt.h>
22 #include <stdlib.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <netdb.h>
28 #include <errno.h>
29 #include <grp.h>
31 #ifndef BASEDIR
32 # ifdef NFS_STATEDIR
33 # define BASEDIR NFS_STATEDIR
34 # else
35 # define BASEDIR "/var/lib/nfs"
36 # endif
37 #endif
39 #define DEFAULT_SM_STATE_PATH BASEDIR "/state"
40 #define DEFAULT_SM_DIR_PATH BASEDIR "/sm"
41 #define DEFAULT_SM_BAK_PATH DEFAULT_SM_DIR_PATH ".bak"
43 char *_SM_BASE_PATH = BASEDIR;
44 char *_SM_STATE_PATH = DEFAULT_SM_STATE_PATH;
45 char *_SM_DIR_PATH = DEFAULT_SM_DIR_PATH;
46 char *_SM_BAK_PATH = DEFAULT_SM_BAK_PATH;
48 #define NSM_PROG 100024
49 #define NSM_PROGRAM 100024
50 #define NSM_VERSION 1
51 #define NSM_TIMEOUT 2
52 #define NSM_NOTIFY 6
53 #define NSM_MAX_TIMEOUT 120 /* don't make this too big */
54 #define MAXMSGSIZE 256
56 struct nsm_host {
57 struct nsm_host * next;
58 char * name;
59 char * path;
60 struct sockaddr_storage addr;
61 struct addrinfo *ai;
62 time_t last_used;
63 time_t send_next;
64 unsigned int timeout;
65 unsigned int retries;
66 unsigned int xid;
69 static char nsm_hostname[256];
70 static uint32_t nsm_state;
71 static int opt_debug = 0;
72 static int opt_quiet = 0;
73 static int opt_update_state = 1;
74 static unsigned int opt_max_retry = 15 * 60;
75 static char * opt_srcaddr = 0;
76 static uint16_t opt_srcport = 0;
77 static int log_syslog = 0;
79 static unsigned int nsm_get_state(int);
80 static void notify(void);
81 static int notify_host(int, struct nsm_host *);
82 static void recv_reply(int);
83 static void backup_hosts(const char *, const char *);
84 static void get_hosts(const char *);
85 static void insert_host(struct nsm_host *);
86 static struct nsm_host *find_host(uint32_t);
87 static void nsm_log(int fac, const char *fmt, ...);
88 static int record_pid(void);
89 static void drop_privs(void);
90 static void set_kernel_nsm_state(int state);
92 static struct nsm_host * hosts = NULL;
95 * Address handling utilities
98 static unsigned short smn_get_port(const struct sockaddr *sap)
100 switch (sap->sa_family) {
101 case AF_INET:
102 return ntohs(((struct sockaddr_in *)sap)->sin_port);
103 case AF_INET6:
104 return ntohs(((struct sockaddr_in6 *)sap)->sin6_port);
106 return 0;
109 static void smn_set_port(struct sockaddr *sap, const unsigned short port)
111 switch (sap->sa_family) {
112 case AF_INET:
113 ((struct sockaddr_in *)sap)->sin_port = htons(port);
114 break;
115 case AF_INET6:
116 ((struct sockaddr_in6 *)sap)->sin6_port = htons(port);
117 break;
121 static struct addrinfo *smn_lookup(const sa_family_t family, const char *name)
123 struct addrinfo *ai, hint = {
124 .ai_family = family,
125 .ai_protocol = IPPROTO_UDP,
128 if (getaddrinfo(name, NULL, &hint, &ai) != 0)
129 return NULL;
131 return ai;
134 static void smn_forget_host(struct nsm_host *host)
136 unlink(host->path);
137 free(host->path);
138 free(host->name);
139 if (host->ai)
140 freeaddrinfo(host->ai);
142 free(host);
146 main(int argc, char **argv)
148 int c;
149 int force = 0;
151 while ((c = getopt(argc, argv, "dm:np:v:qP:f")) != -1) {
152 switch (c) {
153 case 'f':
154 force = 1;
155 break;
156 case 'd':
157 opt_debug++;
158 break;
159 case 'm':
160 opt_max_retry = atoi(optarg) * 60;
161 break;
162 case 'n':
163 opt_update_state = 0;
164 break;
165 case 'p':
166 opt_srcport = atoi(optarg);
167 break;
168 case 'v':
169 opt_srcaddr = optarg;
170 break;
171 case 'q':
172 opt_quiet = 1;
173 break;
174 case 'P':
175 _SM_BASE_PATH = strdup(optarg);
176 _SM_STATE_PATH = malloc(strlen(optarg)+1+sizeof("state"));
177 _SM_DIR_PATH = malloc(strlen(optarg)+1+sizeof("sm"));
178 _SM_BAK_PATH = malloc(strlen(optarg)+1+sizeof("sm.bak"));
179 if (_SM_BASE_PATH == NULL ||
180 _SM_STATE_PATH == NULL ||
181 _SM_DIR_PATH == NULL ||
182 _SM_BAK_PATH == NULL) {
183 nsm_log(LOG_ERR, "unable to allocate memory");
184 exit(1);
186 strcat(strcpy(_SM_STATE_PATH, _SM_BASE_PATH), "/state");
187 strcat(strcpy(_SM_DIR_PATH, _SM_BASE_PATH), "/sm");
188 strcat(strcpy(_SM_BAK_PATH, _SM_BASE_PATH), "/sm.bak");
189 break;
191 default:
192 goto usage;
196 if (optind < argc) {
197 usage: fprintf(stderr,
198 "Usage: sm-notify [-dfq] [-m max-retry-minutes] [-p srcport]\n"
199 " [-P /path/to/state/directory] [-v my_host_name]\n");
200 exit(1);
203 log_syslog = 1;
204 openlog("sm-notify", LOG_PID, LOG_DAEMON);
206 if (strcmp(_SM_BASE_PATH, BASEDIR) == 0) {
207 if (record_pid() == 0 && force == 0 && opt_update_state == 1) {
208 /* already run, don't try again */
209 nsm_log(LOG_NOTICE, "Already notifying clients; Exiting!");
210 exit(0);
214 if (opt_srcaddr) {
215 strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1);
216 } else
217 if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) {
218 nsm_log(LOG_ERR, "Failed to obtain name of local host: %s",
219 strerror(errno));
220 exit(1);
223 backup_hosts(_SM_DIR_PATH, _SM_BAK_PATH);
224 get_hosts(_SM_BAK_PATH);
226 /* If there are not hosts to notify, just exit */
227 if (!hosts) {
228 nsm_log(LOG_DEBUG, "No hosts to notify; exiting");
229 return 0;
232 /* Get and update the NSM state. This will call sync() */
233 nsm_state = nsm_get_state(opt_update_state);
234 set_kernel_nsm_state(nsm_state);
236 if (!opt_debug) {
237 if (!opt_quiet)
238 printf("Backgrounding to notify hosts...\n");
240 if (daemon(0, 0) < 0) {
241 nsm_log(LOG_ERR, "unable to background: %s",
242 strerror(errno));
243 exit(1);
246 close(0);
247 close(1);
248 close(2);
251 notify();
253 if (hosts) {
254 struct nsm_host *hp;
256 while ((hp = hosts) != 0) {
257 hosts = hp->next;
258 nsm_log(LOG_NOTICE,
259 "Unable to notify %s, giving up",
260 hp->name);
262 exit(1);
265 exit(0);
269 * Notify hosts
271 static void
272 notify(void)
274 struct sockaddr_storage address;
275 struct sockaddr *local_addr = (struct sockaddr *)&address;
276 time_t failtime = 0;
277 int sock = -1;
278 int retry_cnt = 0;
280 retry:
281 sock = socket(AF_INET, SOCK_DGRAM, 0);
282 if (sock < 0) {
283 nsm_log(LOG_ERR, "Failed to create RPC socket: %s",
284 strerror(errno));
285 exit(1);
287 fcntl(sock, F_SETFL, O_NONBLOCK);
289 memset(&address, 0, sizeof(address));
290 local_addr->sa_family = AF_INET; /* Default to IPv4 */
292 /* Bind source IP if provided on command line */
293 if (opt_srcaddr) {
294 struct addrinfo *ai = smn_lookup(AF_INET, opt_srcaddr);
295 if (!ai) {
296 nsm_log(LOG_ERR,
297 "Not a valid hostname or address: \"%s\"",
298 opt_srcaddr);
299 exit(1);
302 /* We know it's IPv4 at this point */
303 memcpy(local_addr, ai->ai_addr, ai->ai_addrlen);
305 freeaddrinfo(ai);
308 /* Use source port if provided on the command line,
309 * otherwise use bindresvport */
310 if (opt_srcport) {
311 smn_set_port(local_addr, opt_srcport);
312 if (bind(sock, local_addr, sizeof(struct sockaddr_in)) < 0) {
313 nsm_log(LOG_ERR, "Failed to bind RPC socket: %s",
314 strerror(errno));
315 exit(1);
317 } else {
318 struct servent *se;
319 struct sockaddr_in *sin = (struct sockaddr_in *)local_addr;
320 (void) bindresvport(sock, sin);
321 /* try to avoid known ports */
322 se = getservbyport(sin->sin_port, "udp");
323 if (se && retry_cnt < 100) {
324 retry_cnt++;
325 close(sock);
326 goto retry;
330 if (opt_max_retry)
331 failtime = time(NULL) + opt_max_retry;
333 drop_privs();
335 while (hosts) {
336 struct pollfd pfd;
337 time_t now = time(NULL);
338 unsigned int sent = 0;
339 struct nsm_host *hp;
340 long wait;
342 if (failtime && now >= failtime)
343 break;
345 while (hosts && ((wait = hosts->send_next - now) <= 0)) {
346 /* Never send more than 10 packets at once */
347 if (sent++ >= 10)
348 break;
350 /* Remove queue head */
351 hp = hosts;
352 hosts = hp->next;
354 if (notify_host(sock, hp))
355 continue;
357 /* Set the timeout for this call, using an
358 exponential timeout strategy */
359 wait = hp->timeout;
360 if ((hp->timeout <<= 1) > NSM_MAX_TIMEOUT)
361 hp->timeout = NSM_MAX_TIMEOUT;
362 hp->send_next = now + wait;
363 hp->retries++;
365 insert_host(hp);
367 if (hosts == NULL)
368 return;
370 nsm_log(LOG_DEBUG, "Host %s due in %ld seconds",
371 hosts->name, wait);
373 pfd.fd = sock;
374 pfd.events = POLLIN;
376 wait *= 1000;
377 if (wait < 100)
378 wait = 100;
379 if (poll(&pfd, 1, wait) != 1)
380 continue;
382 recv_reply(sock);
387 * Send notification to a single host
389 static int
390 notify_host(int sock, struct nsm_host *host)
392 struct sockaddr_storage address;
393 struct sockaddr *dest = (struct sockaddr *)&address;
394 socklen_t destlen = sizeof(address);
395 static unsigned int xid = 0;
396 uint32_t msgbuf[MAXMSGSIZE], *p;
397 unsigned int len;
399 if (!xid)
400 xid = getpid() + time(NULL);
401 if (!host->xid)
402 host->xid = xid++;
404 if (host->ai == NULL) {
405 host->ai = smn_lookup(AF_UNSPEC, host->name);
406 if (host->ai == NULL) {
407 nsm_log(LOG_WARNING,
408 "%s doesn't seem to be a valid address,"
409 " skipped", host->name);
410 smn_forget_host(host);
411 return 1;
415 memset(msgbuf, 0, sizeof(msgbuf));
416 p = msgbuf;
417 *p++ = htonl(host->xid);
418 *p++ = 0;
419 *p++ = htonl(2);
421 /* If we retransmitted 4 times, reset the port to force
422 * a new portmap lookup (in case statd was restarted).
423 * We also rotate through multiple IP addresses at this
424 * point.
426 if (host->retries >= 4) {
427 struct addrinfo *first = host->ai;
428 struct addrinfo **next = &host->ai;
430 /* remove the first entry from the list */
431 host->ai = first->ai_next;
432 first->ai_next = NULL;
433 /* find the end of the list */
434 next = &first->ai_next;
435 while ( *next )
436 next = & (*next)->ai_next;
437 /* put first entry at end */
438 *next = first;
439 memcpy(&host->addr, first->ai_addr, first->ai_addrlen);
440 smn_set_port((struct sockaddr *)&host->addr, 0);
441 host->retries = 0;
444 memcpy(dest, &host->addr, destlen);
445 if (smn_get_port(dest) == 0) {
446 /* Build a PMAP packet */
447 nsm_log(LOG_DEBUG, "Sending portmap query to %s", host->name);
449 smn_set_port(dest, 111);
450 *p++ = htonl(100000);
451 *p++ = htonl(2);
452 *p++ = htonl(3);
454 /* Auth and verf */
455 *p++ = 0; *p++ = 0;
456 *p++ = 0; *p++ = 0;
458 *p++ = htonl(NSM_PROGRAM);
459 *p++ = htonl(NSM_VERSION);
460 *p++ = htonl(IPPROTO_UDP);
461 *p++ = 0;
462 } else {
463 /* Build an SM_NOTIFY packet */
464 nsm_log(LOG_DEBUG, "Sending SM_NOTIFY to %s", host->name);
466 *p++ = htonl(NSM_PROGRAM);
467 *p++ = htonl(NSM_VERSION);
468 *p++ = htonl(NSM_NOTIFY);
470 /* Auth and verf */
471 *p++ = 0; *p++ = 0;
472 *p++ = 0; *p++ = 0;
474 /* state change */
475 len = strlen(nsm_hostname);
476 *p++ = htonl(len);
477 memcpy(p, nsm_hostname, len);
478 p += (len + 3) >> 2;
479 *p++ = htonl(nsm_state);
481 len = (p - msgbuf) << 2;
483 if (sendto(sock, msgbuf, len, 0, dest, destlen) < 0)
484 nsm_log(LOG_WARNING, "Sending Reboot Notification to "
485 "'%s' failed: errno %d (%s)", host->name, errno, strerror(errno));
487 return 0;
491 * Receive reply from remote host
493 static void
494 recv_reply(int sock)
496 struct nsm_host *hp;
497 struct sockaddr *sap;
498 uint32_t msgbuf[MAXMSGSIZE], *p, *end;
499 uint32_t xid;
500 int res;
502 res = recv(sock, msgbuf, sizeof(msgbuf), 0);
503 if (res < 0)
504 return;
506 nsm_log(LOG_DEBUG, "Received packet...");
508 p = msgbuf;
509 end = p + (res >> 2);
511 xid = ntohl(*p++);
512 if (*p++ != htonl(1) /* must be REPLY */
513 || *p++ != htonl(0) /* must be ACCEPTED */
514 || *p++ != htonl(0) /* must be NULL verifier */
515 || *p++ != htonl(0)
516 || *p++ != htonl(0)) /* must be SUCCESS */
517 return;
519 /* Before we look at the data, find the host struct for
520 this reply */
521 if ((hp = find_host(xid)) == NULL)
522 return;
523 sap = (struct sockaddr *)&hp->addr;
525 if (smn_get_port(sap) == 0) {
526 /* This was a portmap request */
527 unsigned int port;
529 port = ntohl(*p++);
530 if (p > end)
531 goto fail;
533 hp->send_next = time(NULL);
534 if (port == 0) {
535 /* No binding for statd. Delay the next
536 * portmap query for max timeout */
537 nsm_log(LOG_DEBUG, "No statd on %s", hp->name);
538 hp->timeout = NSM_MAX_TIMEOUT;
539 hp->send_next += NSM_MAX_TIMEOUT;
540 } else {
541 smn_set_port(sap, port);
542 if (hp->timeout >= NSM_MAX_TIMEOUT / 4)
543 hp->timeout = NSM_MAX_TIMEOUT / 4;
545 hp->xid = 0;
546 } else {
547 /* Successful NOTIFY call. Server returns void,
548 * so nothing we need to do here (except
549 * check that we didn't read past the end of the
550 * packet)
552 if (p <= end) {
553 nsm_log(LOG_DEBUG, "Host %s notified successfully",
554 hp->name);
555 smn_forget_host(hp);
556 return;
560 fail: /* Re-insert the host */
561 insert_host(hp);
565 * Back up all hosts from the sm directory to sm.bak
567 static void
568 backup_hosts(const char *dirname, const char *bakname)
570 struct dirent *de;
571 DIR *dir;
573 if (!(dir = opendir(dirname))) {
574 nsm_log(LOG_WARNING,
575 "Failed to open %s: %s", dirname, strerror(errno));
576 return;
579 while ((de = readdir(dir)) != NULL) {
580 char src[1024], dst[1024];
582 if (de->d_name[0] == '.')
583 continue;
585 snprintf(src, sizeof(src), "%s/%s", dirname, de->d_name);
586 snprintf(dst, sizeof(dst), "%s/%s", bakname, de->d_name);
587 if (rename(src, dst) < 0) {
588 nsm_log(LOG_WARNING,
589 "Failed to rename %s -> %s: %m",
590 src, dst);
593 closedir(dir);
597 * Get all entries from sm.bak and convert them to host entries
599 static void
600 get_hosts(const char *dirname)
602 struct nsm_host *host;
603 struct dirent *de;
604 DIR *dir;
606 if (!(dir = opendir(dirname))) {
607 nsm_log(LOG_WARNING,
608 "Failed to open %s: %s", dirname, strerror(errno));
609 return;
612 host = NULL;
613 while ((de = readdir(dir)) != NULL) {
614 struct stat stb;
615 char path[1024];
617 if (de->d_name[0] == '.')
618 continue;
619 if (host == NULL)
620 host = calloc(1, sizeof(*host));
621 if (host == NULL) {
622 nsm_log(LOG_WARNING, "Unable to allocate memory");
623 return;
626 snprintf(path, sizeof(path), "%s/%s", dirname, de->d_name);
627 if (stat(path, &stb) < 0)
628 continue;
630 host->last_used = stb.st_mtime;
631 host->timeout = NSM_TIMEOUT;
632 host->path = strdup(path);
633 host->name = strdup(de->d_name);
634 host->retries = 100; /* force address retry */
636 insert_host(host);
637 host = NULL;
639 closedir(dir);
641 if (host)
642 free(host);
646 * Insert host into sorted list
648 static void
649 insert_host(struct nsm_host *host)
651 struct nsm_host **where, *p;
653 where = &hosts;
654 while ((p = *where) != 0) {
655 /* Sort in ascending order of timeout */
656 if (host->send_next < p->send_next)
657 break;
658 /* If we have the same timeout, put the
659 * most recently used host first.
660 * This makes sure that "recent" hosts
661 * get notified first.
663 if (host->send_next == p->send_next
664 && host->last_used > p->last_used)
665 break;
666 where = &p->next;
669 host->next = *where;
670 *where = host;
674 * Find host given the XID
676 static struct nsm_host *
677 find_host(uint32_t xid)
679 struct nsm_host **where, *p;
681 where = &hosts;
682 while ((p = *where) != 0) {
683 if (p->xid == xid) {
684 *where = p->next;
685 return p;
687 where = &p->next;
689 return NULL;
694 * Retrieve the current NSM state
696 static unsigned int
697 nsm_get_state(int update)
699 char newfile[PATH_MAX];
700 int fd, state;
702 if ((fd = open(_SM_STATE_PATH, O_RDONLY)) < 0) {
703 if (!opt_quiet) {
704 nsm_log(LOG_WARNING, "%s: %m", _SM_STATE_PATH);
705 nsm_log(LOG_WARNING, "Creating %s, set initial state 1",
706 _SM_STATE_PATH);
708 state = 1;
709 update = 1;
710 } else {
711 if (read(fd, &state, sizeof(state)) != sizeof(state)) {
712 nsm_log(LOG_WARNING,
713 "%s: bad file size, setting state = 1",
714 _SM_STATE_PATH);
715 state = 1;
716 update = 1;
717 } else {
718 if (!(state & 1))
719 state += 1;
721 close(fd);
724 if (update) {
725 state += 2;
726 snprintf(newfile, sizeof(newfile),
727 "%s.new", _SM_STATE_PATH);
728 if ((fd = open(newfile, O_CREAT|O_WRONLY, 0644)) < 0) {
729 nsm_log(LOG_ERR, "Cannot create %s: %m", newfile);
730 exit(1);
732 if (write(fd, &state, sizeof(state)) != sizeof(state)) {
733 nsm_log(LOG_ERR,
734 "Failed to write state to %s", newfile);
735 exit(1);
737 close(fd);
738 if (rename(newfile, _SM_STATE_PATH) < 0) {
739 nsm_log(LOG_ERR,
740 "Cannot create %s: %m", _SM_STATE_PATH);
741 exit(1);
743 sync();
746 return state;
750 * Log a message
752 static void
753 nsm_log(int fac, const char *fmt, ...)
755 va_list ap;
757 if (fac == LOG_DEBUG && !opt_debug)
758 return;
760 va_start(ap, fmt);
761 if (log_syslog)
762 vsyslog(fac, fmt, ap);
763 else {
764 vfprintf(stderr, fmt, ap);
765 fputs("\n", stderr);
767 va_end(ap);
771 * Record pid in /var/run/sm-notify.pid
772 * This file should remain until a reboot, even if the
773 * program exits.
774 * If file already exists, fail.
776 static int record_pid(void)
778 char pid[20];
779 int fd;
781 snprintf(pid, 20, "%d\n", getpid());
782 fd = open("/var/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600);
783 if (fd < 0)
784 return 0;
785 write(fd, pid, strlen(pid));
786 close(fd);
787 return 1;
790 /* Drop privileges to match owner of state-directory
791 * (in case a reply triggers some unknown bug).
793 static void drop_privs(void)
795 struct stat st;
797 if (stat(_SM_DIR_PATH, &st) == -1 &&
798 stat(_SM_BASE_PATH, &st) == -1) {
799 st.st_uid = 0;
800 st.st_gid = 0;
803 if (st.st_uid == 0) {
804 nsm_log(LOG_WARNING,
805 "sm-notify running as root. chown %s to choose different user",
806 _SM_DIR_PATH);
807 return;
810 setgroups(0, NULL);
811 if (setgid(st.st_gid) == -1
812 || setuid(st.st_uid) == -1) {
813 nsm_log(LOG_ERR, "Fail to drop privileges");
814 exit(1);
818 static void set_kernel_nsm_state(int state)
820 int fd;
822 fd = open("/proc/sys/fs/nfs/nsm_local_state",O_WRONLY);
823 if (fd >= 0) {
824 char buf[20];
825 snprintf(buf, sizeof(buf), "%d", state);
826 write(fd, buf, strlen(buf));
827 close(fd);