X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=utils%2Fstatd%2Fsm-notify.c;h=1f490b0e6b7832baadf963f10c5839c846f4e1e9;hp=d8e2c011701b05a539ba4c65ea83545541204542;hb=7869a76207d3f4b3bd4ab57b4a7a8807ac2ff0c6;hpb=5d8b800b8438222a55c4698c86b558b15717aa21 diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c index d8e2c01..1f490b0 100644 --- a/utils/statd/sm-notify.c +++ b/utils/statd/sm-notify.c @@ -8,6 +8,7 @@ #include #endif +#include #include #include #include @@ -28,116 +29,335 @@ #include #include -#ifndef BASEDIR -# ifdef NFS_STATEDIR -# define BASEDIR NFS_STATEDIR -# else -# define BASEDIR "/var/lib/nfs" -# endif -#endif - -#define DEFAULT_SM_STATE_PATH BASEDIR "/state" -#define DEFAULT_SM_DIR_PATH BASEDIR "/sm" -#define DEFAULT_SM_BAK_PATH DEFAULT_SM_DIR_PATH ".bak" +#include "sockaddr.h" +#include "xlog.h" +#include "nsm.h" +#include "nfsrpc.h" -char *_SM_BASE_PATH = BASEDIR; -char *_SM_STATE_PATH = DEFAULT_SM_STATE_PATH; -char *_SM_DIR_PATH = DEFAULT_SM_DIR_PATH; -char *_SM_BAK_PATH = DEFAULT_SM_BAK_PATH; +/* glibc before 2.3.4 */ +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0 +#endif -#define NSM_PROG 100024 -#define NSM_PROGRAM 100024 -#define NSM_VERSION 1 #define NSM_TIMEOUT 2 -#define NSM_NOTIFY 6 #define NSM_MAX_TIMEOUT 120 /* don't make this too big */ -#define MAXMSGSIZE 256 struct nsm_host { struct nsm_host * next; char * name; - char * path; - struct sockaddr_storage addr; + char * mon_name; + char * my_name; struct addrinfo *ai; time_t last_used; time_t send_next; unsigned int timeout; unsigned int retries; - unsigned int xid; + uint32_t xid; }; -static char nsm_hostname[256]; -static uint32_t nsm_state; +static char nsm_hostname[SM_MAXSTRLEN + 1]; +static int nsm_state; +static int nsm_family = AF_INET; static int opt_debug = 0; -static int opt_quiet = 0; -static int opt_update_state = 1; +static _Bool opt_update_state = true; static unsigned int opt_max_retry = 15 * 60; -static char * opt_srcaddr = 0; -static uint16_t opt_srcport = 0; -static int log_syslog = 0; +static char * opt_srcaddr = NULL; +static char * opt_srcport = NULL; -static unsigned int nsm_get_state(int); -static void notify(void); +static void notify(const int sock); static int notify_host(int, struct nsm_host *); static void recv_reply(int); -static void backup_hosts(const char *, const char *); -static void get_hosts(const char *); static void insert_host(struct nsm_host *); static struct nsm_host *find_host(uint32_t); -static void nsm_log(int fac, const char *fmt, ...); static int record_pid(void); -static void drop_privs(void); -static void set_kernel_nsm_state(int state); static struct nsm_host * hosts = NULL; -/* - * Address handling utilities - */ +__attribute_malloc__ +static struct addrinfo * +smn_lookup(const char *name) +{ + struct addrinfo *ai = NULL; + struct addrinfo hint = { + .ai_family = (nsm_family == AF_INET ? AF_INET: AF_UNSPEC), + .ai_protocol = (int)IPPROTO_UDP, + }; + int error; -static unsigned short smn_get_port(const struct sockaddr *sap) + error = getaddrinfo(name, NULL, &hint, &ai); + if (error != 0) { + xlog(D_GENERAL, "getaddrinfo(3): %s", gai_strerror(error)); + return NULL; + } + + return ai; +} + +__attribute_malloc__ +static struct nsm_host * +smn_alloc_host(const char *hostname, const char *mon_name, + const char *my_name, const time_t timestamp) { - switch (sap->sa_family) { - case AF_INET: - return ntohs(((struct sockaddr_in *)sap)->sin_port); - case AF_INET6: - return ntohs(((struct sockaddr_in6 *)sap)->sin6_port); + struct nsm_host *host; + + host = calloc(1, sizeof(*host)); + if (host == NULL) + goto out_nomem; + + host->name = strdup(hostname); + host->mon_name = strdup(mon_name); + host->my_name = strdup(my_name); + if (host->name == NULL || + host->mon_name == NULL || + host->my_name == NULL) { + free(host->my_name); + free(host->mon_name); + free(host->name); + free(host); + goto out_nomem; } - return 0; + + host->last_used = timestamp; + host->timeout = NSM_TIMEOUT; + host->retries = 100; /* force address retry */ + + return host; + +out_nomem: + xlog_warn("Unable to allocate memory"); + return NULL; } -static void smn_set_port(struct sockaddr *sap, const unsigned short port) +static void smn_forget_host(struct nsm_host *host) { - switch (sap->sa_family) { - case AF_INET: - ((struct sockaddr_in *)sap)->sin_port = htons(port); - break; - case AF_INET6: - ((struct sockaddr_in6 *)sap)->sin6_port = htons(port); - break; + xlog(D_CALL, "Removing %s (%s, %s) from notify list", + host->name, host->mon_name, host->my_name); + + nsm_delete_notified_host(host->name, host->mon_name, host->my_name); + + free(host->my_name); + free(host->mon_name); + free(host->name); + if (host->ai) + freeaddrinfo(host->ai); + + free(host); +} + +static unsigned int +smn_get_host(const char *hostname, + __attribute__ ((unused)) const struct sockaddr *sap, + const struct mon *m, const time_t timestamp) +{ + struct nsm_host *host; + + host = smn_alloc_host(hostname, + m->mon_id.mon_name, m->mon_id.my_id.my_name, timestamp); + if (host == NULL) + return 0; + + insert_host(host); + xlog(D_GENERAL, "Added host %s to notify list", hostname); + return 1; +} + +#ifdef IPV6_SUPPORTED +static int smn_socket(void) +{ + int sock; + + /* + * Use an AF_INET socket if IPv6 is disabled on the + * local system. + */ + sock = socket(AF_INET6, SOCK_DGRAM, 0); + if (sock == -1) { + if (errno != EAFNOSUPPORT) { + xlog(L_ERROR, "Failed to create RPC socket: %m"); + return -1; + } + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + xlog(L_ERROR, "Failed to create RPC socket: %m"); + return -1; + } + } else + nsm_family = AF_INET6; + + if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { + xlog(L_ERROR, "fcntl(3) on RPC socket failed: %m"); + goto out_close; } + + /* + * TI-RPC over IPv6 (udp6/tcp6) does not handle IPv4. However, + * since sm-notify open-codes all of its RPC support, it can + * use a single socket and let the local network stack provide + * the correct mapping between address families automatically. + * This is the same thing that is done in the kernel. + */ + if (nsm_family == AF_INET6) { + const int zero = 0; + socklen_t zerolen = (socklen_t)sizeof(zero); + + if (setsockopt(sock, SOL_IPV6, IPV6_V6ONLY, + (char *)&zero, zerolen) == -1) { + xlog(L_ERROR, "setsockopt(3) on RPC socket failed: %m"); + goto out_close; + } + } + + return sock; + +out_close: + (void)close(sock); + return -1; +} +#else /* !IPV6_SUPPORTED */ +static int smn_socket(void) +{ + int sock; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == -1) { + xlog(L_ERROR, "Failed to create RPC socket: %m"); + return -1; + } + + if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { + xlog(L_ERROR, "fcntl(3) on RPC socket failed: %m"); + (void)close(sock); + return -1; + } + + return sock; } +#endif /* !IPV6_SUPPORTED */ -static struct addrinfo *smn_lookup(const sa_family_t family, const char *name) +/* + * If admin specified a source address or srcport, then convert those + * to a sockaddr and return it. Otherwise, return an ANYADDR address. + */ +__attribute_malloc__ +static struct addrinfo * +smn_bind_address(const char *srcaddr, const char *srcport) { - struct addrinfo *ai, hint = { - .ai_family = family, - .ai_protocol = IPPROTO_UDP, + struct addrinfo *ai = NULL; + struct addrinfo hint = { + .ai_flags = AI_NUMERICSERV, + .ai_family = nsm_family, + .ai_protocol = (int)IPPROTO_UDP, }; - - if (getaddrinfo(name, NULL, &hint, &ai) != 0) + int error; + + if (srcaddr == NULL) + hint.ai_flags |= AI_PASSIVE; + + /* Do not allow "node" and "service" parameters both to be NULL */ + if (srcport == NULL) + error = getaddrinfo(srcaddr, "", &hint, &ai); + else + error = getaddrinfo(srcaddr, srcport, &hint, &ai); + if (error != 0) { + xlog(L_ERROR, + "Invalid bind address or port for RPC socket: %s", + gai_strerror(error)); return NULL; + } return ai; } +#ifdef HAVE_LIBTIRPC +static int +smn_bindresvport(int sock, struct sockaddr *sap) +{ + return bindresvport_sa(sock, sap); +} + +#else /* !HAVE_LIBTIRPC */ +static int +smn_bindresvport(int sock, struct sockaddr *sap) +{ + if (sap->sa_family != AF_INET) { + errno = EAFNOSUPPORT; + return -1; + } + + return bindresvport(sock, (struct sockaddr_in *)(char *)sap); +} +#endif /* !HAVE_LIBTIRPC */ + +/* + * Prepare a socket for sending RPC requests + * + * Returns a bound datagram socket file descriptor, or -1 if + * an error occurs. + */ +static int +smn_create_socket(const char *srcaddr, const char *srcport) +{ + int sock, retry_cnt = 0; + struct addrinfo *ai; + +retry: + sock = smn_socket(); + if (sock == -1) + return -1; + + ai = smn_bind_address(srcaddr, srcport); + if (ai == NULL) { + (void)close(sock); + return -1; + } + + /* Use source port if provided on the command line, + * otherwise use bindresvport */ + if (srcport) { + if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) { + xlog(L_ERROR, "Failed to bind RPC socket: %m"); + freeaddrinfo(ai); + (void)close(sock); + return -1; + } + } else { + struct servent *se; + + if (smn_bindresvport(sock, ai->ai_addr) == -1) { + xlog(L_ERROR, + "bindresvport on RPC socket failed: %m"); + freeaddrinfo(ai); + (void)close(sock); + return -1; + } + + /* try to avoid known ports */ + se = getservbyport((int)nfs_get_port(ai->ai_addr), "udp"); + if (se != NULL && retry_cnt < 100) { + retry_cnt++; + freeaddrinfo(ai); + (void)close(sock); + goto retry; + } + } + + freeaddrinfo(ai); + return sock; +} + int main(int argc, char **argv) { - int c; - int force = 0; + int c, sock, force = 0; + char * progname; + + progname = strrchr(argv[0], '/'); + if (progname != NULL) + progname++; + else + progname = argv[0]; - while ((c = getopt(argc, argv, "dm:np:v:qP:f")) != -1) { + while ((c = getopt(argc, argv, "dm:np:v:P:f")) != -1) { switch (c) { case 'f': force = 1; @@ -149,32 +369,17 @@ main(int argc, char **argv) opt_max_retry = atoi(optarg) * 60; break; case 'n': - opt_update_state = 0; + opt_update_state = false; break; case 'p': - opt_srcport = atoi(optarg); + opt_srcport = optarg; break; case 'v': opt_srcaddr = optarg; break; - case 'q': - opt_quiet = 1; - break; case 'P': - _SM_BASE_PATH = strdup(optarg); - _SM_STATE_PATH = malloc(strlen(optarg)+1+sizeof("state")); - _SM_DIR_PATH = malloc(strlen(optarg)+1+sizeof("sm")); - _SM_BAK_PATH = malloc(strlen(optarg)+1+sizeof("sm.bak")); - if (_SM_BASE_PATH == NULL || - _SM_STATE_PATH == NULL || - _SM_DIR_PATH == NULL || - _SM_BAK_PATH == NULL) { - nsm_log(LOG_ERR, "unable to allocate memory"); + if (!nsm_setup_pathnames(argv[0], optarg)) exit(1); - } - strcat(strcpy(_SM_STATE_PATH, _SM_BASE_PATH), "/state"); - strcat(strcpy(_SM_DIR_PATH, _SM_BASE_PATH), "/sm"); - strcat(strcpy(_SM_BAK_PATH, _SM_BASE_PATH), "/sm.bak"); break; default: @@ -184,51 +389,75 @@ main(int argc, char **argv) if (optind < argc) { usage: fprintf(stderr, - "Usage: sm-notify [-dfq] [-m max-retry-minutes] [-p srcport]\n" - " [-P /path/to/state/directory] [-v my_host_name]\n"); + "Usage: %s -notify [-dfq] [-m max-retry-minutes] [-p srcport]\n" + " [-P /path/to/state/directory] [-v my_host_name]\n", + progname); exit(1); } - log_syslog = 1; - openlog("sm-notify", LOG_PID, LOG_DAEMON); + xlog_syslog(1); + if (opt_debug) { + xlog_stderr(1); + xlog_config(D_ALL, 1); + } else + xlog_stderr(0); + + xlog_open(progname); + xlog(L_NOTICE, "Version " VERSION " starting"); - if (strcmp(_SM_BASE_PATH, BASEDIR) == 0) { - if (record_pid() == 0 && force == 0 && opt_update_state == 1) { + if (nsm_is_default_parentdir()) { + if (record_pid() == 0 && force == 0 && opt_update_state) { /* already run, don't try again */ - nsm_log(LOG_NOTICE, "Already notifying clients; Exiting!"); + xlog(L_NOTICE, "Already notifying clients; Exiting!"); exit(0); } } - if (opt_srcaddr) { - strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1); - } else - if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) { - nsm_log(LOG_ERR, "Failed to obtain name of local host: %s", - strerror(errno)); - exit(1); + if (opt_srcaddr != NULL) { + struct addrinfo *ai = NULL; + struct addrinfo hint = { + .ai_family = AF_UNSPEC, + .ai_flags = AI_NUMERICHOST, + }; + + if (getaddrinfo(opt_srcaddr, NULL, &hint, &ai)) + /* not a presentation address - use it */ + strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)); + else { + /* was a presentation address - look it up in + * /etc/hosts, so it can be used for my_name */ + int error; + + freeaddrinfo(ai); + hint.ai_flags = AI_CANONNAME; + error = getaddrinfo(opt_srcaddr, NULL, &hint, &ai); + if (error != 0) { + xlog(L_ERROR, "Bind address %s is unusable: %s", + opt_srcaddr, gai_strerror(error)); + exit(1); + } + strncpy(nsm_hostname, ai->ai_canonname, + sizeof(nsm_hostname)); + freeaddrinfo(ai); + } } - backup_hosts(_SM_DIR_PATH, _SM_BAK_PATH); - get_hosts(_SM_BAK_PATH); - - /* If there are not hosts to notify, just exit */ - if (!hosts) { - nsm_log(LOG_DEBUG, "No hosts to notify; exiting"); + (void)nsm_retire_monitored_hosts(); + if (nsm_load_notify_list(smn_get_host) == 0) { + xlog(D_GENERAL, "No hosts to notify; exiting"); return 0; } - /* Get and update the NSM state. This will call sync() */ nsm_state = nsm_get_state(opt_update_state); - set_kernel_nsm_state(nsm_state); + if (nsm_state == 0) + exit(1); + nsm_update_kernel_state(nsm_state); if (!opt_debug) { - if (!opt_quiet) - printf("Backgrounding to notify hosts...\n"); + xlog(L_NOTICE, "Backgrounding to notify hosts...\n"); if (daemon(0, 0) < 0) { - nsm_log(LOG_ERR, "unable to background: %s", - strerror(errno)); + xlog(L_ERROR, "unable to background: %m"); exit(1); } @@ -237,15 +466,21 @@ usage: fprintf(stderr, close(2); } - notify(); + sock = smn_create_socket(opt_srcaddr, opt_srcport); + if (sock == -1) + exit(1); + + if (!nsm_drop_privileges(-1)) + exit(1); + + notify(sock); if (hosts) { struct nsm_host *hp; while ((hp = hosts) != 0) { hosts = hp->next; - nsm_log(LOG_NOTICE, - "Unable to notify %s, giving up", + xlog(L_NOTICE, "Unable to notify %s, giving up", hp->name); } exit(1); @@ -258,69 +493,13 @@ usage: fprintf(stderr, * Notify hosts */ static void -notify(void) +notify(const int sock) { - struct sockaddr_storage address; - struct sockaddr *local_addr = (struct sockaddr *)&address; time_t failtime = 0; - int sock = -1; - int retry_cnt = 0; - - retry: - sock = socket(AF_INET, SOCK_DGRAM, 0); - if (sock < 0) { - nsm_log(LOG_ERR, "Failed to create RPC socket: %s", - strerror(errno)); - exit(1); - } - fcntl(sock, F_SETFL, O_NONBLOCK); - - memset(&address, 0, sizeof(address)); - local_addr->sa_family = AF_INET; /* Default to IPv4 */ - - /* Bind source IP if provided on command line */ - if (opt_srcaddr) { - struct addrinfo *ai = smn_lookup(AF_INET, opt_srcaddr); - if (!ai) { - nsm_log(LOG_ERR, - "Not a valid hostname or address: \"%s\"", - opt_srcaddr); - exit(1); - } - - /* We know it's IPv4 at this point */ - memcpy(local_addr, ai->ai_addr, ai->ai_addrlen); - - freeaddrinfo(ai); - } - - /* Use source port if provided on the command line, - * otherwise use bindresvport */ - if (opt_srcport) { - smn_set_port(local_addr, opt_srcport); - if (bind(sock, local_addr, sizeof(struct sockaddr_in)) < 0) { - nsm_log(LOG_ERR, "Failed to bind RPC socket: %s", - strerror(errno)); - exit(1); - } - } else { - struct servent *se; - struct sockaddr_in *sin = (struct sockaddr_in *)local_addr; - (void) bindresvport(sock, sin); - /* try to avoid known ports */ - se = getservbyport(sin->sin_port, "udp"); - if (se && retry_cnt < 100) { - retry_cnt++; - close(sock); - goto retry; - } - } if (opt_max_retry) failtime = time(NULL) + opt_max_retry; - drop_privs(); - while (hosts) { struct pollfd pfd; time_t now = time(NULL); @@ -340,13 +519,8 @@ notify(void) hp = hosts; hosts = hp->next; - if (notify_host(sock, hp)){ - unlink(hp->path); - free(hp->name); - free(hp->path); - free(hp); + if (notify_host(sock, hp)) continue; - } /* Set the timeout for this call, using an exponential timeout strategy */ @@ -361,7 +535,7 @@ notify(void) if (hosts == NULL) return; - nsm_log(LOG_DEBUG, "Host %s due in %ld seconds", + xlog(D_GENERAL, "Host %s due in %ld seconds", hosts->name, wait); pfd.fd = sock; @@ -383,260 +557,147 @@ notify(void) static int notify_host(int sock, struct nsm_host *host) { - struct sockaddr_storage address; - struct sockaddr *dest = (struct sockaddr *)&address; - socklen_t destlen = sizeof(address); - static unsigned int xid = 0; - uint32_t msgbuf[MAXMSGSIZE], *p; - unsigned int len; - - if (!xid) - xid = getpid() + time(NULL); - if (!host->xid) - host->xid = xid++; + const char *my_name = (opt_srcaddr != NULL ? + nsm_hostname : host->my_name); + struct sockaddr *sap; + socklen_t salen; if (host->ai == NULL) { - host->ai = smn_lookup(AF_UNSPEC, host->name); + host->ai = smn_lookup(host->name); if (host->ai == NULL) { - nsm_log(LOG_WARNING, - "%s doesn't seem to be a valid address," - " skipped", host->name); - return 1; + xlog_warn("DNS resolution of %s failed; " + "retrying later", host->name); + return 0; } } - memset(msgbuf, 0, sizeof(msgbuf)); - p = msgbuf; - *p++ = htonl(host->xid); - *p++ = 0; - *p++ = htonl(2); - /* If we retransmitted 4 times, reset the port to force * a new portmap lookup (in case statd was restarted). * We also rotate through multiple IP addresses at this * point. */ if (host->retries >= 4) { - struct addrinfo *first = host->ai; - struct addrinfo **next = &host->ai; - - /* remove the first entry from the list */ - host->ai = first->ai_next; - first->ai_next = NULL; - /* find the end of the list */ - next = &first->ai_next; - while ( *next ) - next = & (*next)->ai_next; - /* put first entry at end */ - *next = first; - memcpy(&host->addr, first->ai_addr, first->ai_addrlen); - smn_set_port((struct sockaddr *)&host->addr, 0); + /* don't rotate if there is only one addrinfo */ + if (host->ai->ai_next != NULL) { + struct addrinfo *first = host->ai; + struct addrinfo **next = &host->ai; + + /* remove the first entry from the list */ + host->ai = first->ai_next; + first->ai_next = NULL; + /* find the end of the list */ + next = &first->ai_next; + while ( *next ) + next = & (*next)->ai_next; + /* put first entry at end */ + *next = first; + } + + nfs_set_port(host->ai->ai_addr, 0); host->retries = 0; } - memcpy(dest, &host->addr, destlen); - if (smn_get_port(dest) == 0) { - /* Build a PMAP packet */ - nsm_log(LOG_DEBUG, "Sending portmap query to %s", host->name); + sap = host->ai->ai_addr; + salen = host->ai->ai_addrlen; - smn_set_port(dest, 111); - *p++ = htonl(100000); - *p++ = htonl(2); - *p++ = htonl(3); - - /* Auth and verf */ - *p++ = 0; *p++ = 0; - *p++ = 0; *p++ = 0; - - *p++ = htonl(NSM_PROGRAM); - *p++ = htonl(NSM_VERSION); - *p++ = htonl(IPPROTO_UDP); - *p++ = 0; - } else { - /* Build an SM_NOTIFY packet */ - nsm_log(LOG_DEBUG, "Sending SM_NOTIFY to %s", host->name); - - *p++ = htonl(NSM_PROGRAM); - *p++ = htonl(NSM_VERSION); - *p++ = htonl(NSM_NOTIFY); - - /* Auth and verf */ - *p++ = 0; *p++ = 0; - *p++ = 0; *p++ = 0; - - /* state change */ - len = strlen(nsm_hostname); - *p++ = htonl(len); - memcpy(p, nsm_hostname, len); - p += (len + 3) >> 2; - *p++ = htonl(nsm_state); - } - len = (p - msgbuf) << 2; + if (nfs_get_port(sap) == 0) + host->xid = nsm_xmit_rpcbind(sock, sap, SM_PROG, SM_VERS); + else + host->xid = nsm_xmit_notify(sock, sap, salen, + SM_PROG, my_name, nsm_state); - if (sendto(sock, msgbuf, len, 0, dest, destlen) < 0) - nsm_log(LOG_WARNING, "Sending Reboot Notification to " - "'%s' failed: errno %d (%s)", host->name, errno, strerror(errno)); - return 0; } /* - * Receive reply from remote host + * Extract the returned port number and set up the SM_NOTIFY call. */ static void -recv_reply(int sock) +recv_rpcbind_reply(struct sockaddr *sap, struct nsm_host *host, XDR *xdr) { - struct nsm_host *hp; - struct sockaddr *sap; - uint32_t msgbuf[MAXMSGSIZE], *p, *end; - uint32_t xid; - int res; - - res = recv(sock, msgbuf, sizeof(msgbuf), 0); - if (res < 0) - return; + uint16_t port = nsm_recv_rpcbind(sap->sa_family, xdr); - nsm_log(LOG_DEBUG, "Received packet..."); + host->send_next = time(NULL); + host->xid = 0; - p = msgbuf; - end = p + (res >> 2); - - xid = ntohl(*p++); - if (*p++ != htonl(1) /* must be REPLY */ - || *p++ != htonl(0) /* must be ACCEPTED */ - || *p++ != htonl(0) /* must be NULL verifier */ - || *p++ != htonl(0) - || *p++ != htonl(0)) /* must be SUCCESS */ - return; - - /* Before we look at the data, find the host struct for - this reply */ - if ((hp = find_host(xid)) == NULL) - return; - sap = (struct sockaddr *)&hp->addr; - - if (smn_get_port(sap) == 0) { - /* This was a portmap request */ - unsigned int port; - - port = ntohl(*p++); - if (p > end) - goto fail; - - hp->send_next = time(NULL); - if (port == 0) { - /* No binding for statd. Delay the next - * portmap query for max timeout */ - nsm_log(LOG_DEBUG, "No statd on %s", hp->name); - hp->timeout = NSM_MAX_TIMEOUT; - hp->send_next += NSM_MAX_TIMEOUT; - } else { - smn_set_port(sap, port); - if (hp->timeout >= NSM_MAX_TIMEOUT / 4) - hp->timeout = NSM_MAX_TIMEOUT / 4; - } - hp->xid = 0; + if (port == 0) { + /* No binding for statd... */ + xlog(D_GENERAL, "No statd on host %s", host->name); + host->timeout = NSM_MAX_TIMEOUT; + host->send_next += NSM_MAX_TIMEOUT; } else { - /* Successful NOTIFY call. Server returns void, - * so nothing we need to do here (except - * check that we didn't read past the end of the - * packet) - */ - if (p <= end) { - nsm_log(LOG_DEBUG, "Host %s notified successfully", - hp->name); - unlink(hp->path); - free(hp->name); - free(hp->path); - free(hp); - freeaddrinfo(hp->ai); - return; - } + nfs_set_port(sap, port); + if (host->timeout >= NSM_MAX_TIMEOUT / 4) + host->timeout = NSM_MAX_TIMEOUT / 4; } -fail: /* Re-insert the host */ - insert_host(hp); + insert_host(host); } /* - * Back up all hosts from the sm directory to sm.bak + * Successful NOTIFY call. Server returns void. + * + * Try sending another SM_NOTIFY with an unqualified "my_name" + * argument. Reuse the port number. If "my_name" is already + * unqualified, we're done. */ static void -backup_hosts(const char *dirname, const char *bakname) +recv_notify_reply(struct nsm_host *host) { - struct dirent *de; - DIR *dir; - - if (!(dir = opendir(dirname))) { - nsm_log(LOG_WARNING, - "Failed to open %s: %s", dirname, strerror(errno)); - return; - } - - while ((de = readdir(dir)) != NULL) { - char src[1024], dst[1024]; - - if (de->d_name[0] == '.') - continue; - - snprintf(src, sizeof(src), "%s/%s", dirname, de->d_name); - snprintf(dst, sizeof(dst), "%s/%s", bakname, de->d_name); - if (rename(src, dst) < 0) { - nsm_log(LOG_WARNING, - "Failed to rename %s -> %s: %m", - src, dst); - } + char *dot = strchr(host->my_name, '.'); + + if (dot != NULL) { + *dot = '\0'; + host->send_next = time(NULL); + host->xid = 0; + if (host->timeout >= NSM_MAX_TIMEOUT / 4) + host->timeout = NSM_MAX_TIMEOUT / 4; + insert_host(host); + } else { + xlog(D_GENERAL, "Host %s notified successfully", host->name); + smn_forget_host(host); } - closedir(dir); } /* - * Get all entries from sm.bak and convert them to host entries + * Receive reply from remote host */ static void -get_hosts(const char *dirname) +recv_reply(int sock) { - struct nsm_host *host; - struct dirent *de; - DIR *dir; + struct nsm_host *hp; + struct sockaddr *sap; + char msgbuf[NSM_MAXMSGSIZE]; + uint32_t xid; + ssize_t msglen; + XDR xdr; - if (!(dir = opendir(dirname))) { - nsm_log(LOG_WARNING, - "Failed to open %s: %s", dirname, strerror(errno)); + memset(msgbuf, 0 , sizeof(msgbuf)); + msglen = recv(sock, msgbuf, sizeof(msgbuf), 0); + if (msglen < 0) return; - } - host = NULL; - while ((de = readdir(dir)) != NULL) { - struct stat stb; - char path[1024]; + xlog(D_GENERAL, "Received packet..."); - if (de->d_name[0] == '.') - continue; - if (host == NULL) - host = calloc(1, sizeof(*host)); - if (host == NULL) { - nsm_log(LOG_WARNING, "Unable to allocate memory"); - return; - } + memset(&xdr, 0, sizeof(xdr)); + xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE); + xid = nsm_parse_reply(&xdr); + if (xid == 0) + goto out; - snprintf(path, sizeof(path), "%s/%s", dirname, de->d_name); - if (stat(path, &stb) < 0) - continue; + /* Before we look at the data, find the host struct for + this reply */ + if ((hp = find_host(xid)) == NULL) + goto out; - host->last_used = stb.st_mtime; - host->timeout = NSM_TIMEOUT; - host->path = strdup(path); - host->name = strdup(de->d_name); - host->retries = 100; /* force address retry */ + sap = hp->ai->ai_addr; + if (nfs_get_port(sap) == 0) + recv_rpcbind_reply(sap, hp, &xdr); + else + recv_notify_reply(hp); - insert_host(host); - host = NULL; - } - closedir(dir); - - if (host) - free(host); +out: + xdr_destroy(&xdr); } /* @@ -686,84 +747,6 @@ find_host(uint32_t xid) return NULL; } - -/* - * Retrieve the current NSM state - */ -static unsigned int -nsm_get_state(int update) -{ - char newfile[PATH_MAX]; - int fd, state; - - if ((fd = open(_SM_STATE_PATH, O_RDONLY)) < 0) { - if (!opt_quiet) { - nsm_log(LOG_WARNING, "%s: %m", _SM_STATE_PATH); - nsm_log(LOG_WARNING, "Creating %s, set initial state 1", - _SM_STATE_PATH); - } - state = 1; - update = 1; - } else { - if (read(fd, &state, sizeof(state)) != sizeof(state)) { - nsm_log(LOG_WARNING, - "%s: bad file size, setting state = 1", - _SM_STATE_PATH); - state = 1; - update = 1; - } else { - if (!(state & 1)) - state += 1; - } - close(fd); - } - - if (update) { - state += 2; - snprintf(newfile, sizeof(newfile), - "%s.new", _SM_STATE_PATH); - if ((fd = open(newfile, O_CREAT|O_WRONLY, 0644)) < 0) { - nsm_log(LOG_ERR, "Cannot create %s: %m", newfile); - exit(1); - } - if (write(fd, &state, sizeof(state)) != sizeof(state)) { - nsm_log(LOG_ERR, - "Failed to write state to %s", newfile); - exit(1); - } - close(fd); - if (rename(newfile, _SM_STATE_PATH) < 0) { - nsm_log(LOG_ERR, - "Cannot create %s: %m", _SM_STATE_PATH); - exit(1); - } - sync(); - } - - return state; -} - -/* - * Log a message - */ -static void -nsm_log(int fac, const char *fmt, ...) -{ - va_list ap; - - if (fac == LOG_DEBUG && !opt_debug) - return; - - va_start(ap, fmt); - if (log_syslog) - vsyslog(fac, fmt, ap); - else { - vfprintf(stderr, fmt, ap); - fputs("\n", stderr); - } - va_end(ap); -} - /* * Record pid in /var/run/sm-notify.pid * This file should remain until a reboot, even if the @@ -773,54 +756,20 @@ nsm_log(int fac, const char *fmt, ...) static int record_pid(void) { char pid[20]; + ssize_t len; int fd; - snprintf(pid, 20, "%d\n", getpid()); + (void)snprintf(pid, sizeof(pid), "%d\n", (int)getpid()); fd = open("/var/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600); if (fd < 0) return 0; - write(fd, pid, strlen(pid)); - close(fd); - return 1; -} -/* Drop privileges to match owner of state-directory - * (in case a reply triggers some unknown bug). - */ -static void drop_privs(void) -{ - struct stat st; - - if (stat(_SM_DIR_PATH, &st) == -1 && - stat(_SM_BASE_PATH, &st) == -1) { - st.st_uid = 0; - st.st_gid = 0; - } - - if (st.st_uid == 0) { - nsm_log(LOG_WARNING, - "sm-notify running as root. chown %s to choose different user", - _SM_DIR_PATH); - return; - } - - setgroups(0, NULL); - if (setgid(st.st_gid) == -1 - || setuid(st.st_uid) == -1) { - nsm_log(LOG_ERR, "Fail to drop privileges"); - exit(1); + len = write(fd, pid, strlen(pid)); + if ((len < 0) || ((size_t)len != strlen(pid))) { + xlog_warn("Writing to pid file failed: errno %d (%m)", + errno); } -} -static void set_kernel_nsm_state(int state) -{ - int fd; - - fd = open("/proc/sys/fs/nfs/nsm_local_state",O_WRONLY); - if (fd >= 0) { - char buf[20]; - snprintf(buf, sizeof(buf), "%d", state); - write(fd, buf, strlen(buf)); - close(fd); - } + (void)close(fd); + return 1; }