X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=utils%2Fstatd%2Fsm-notify.c;h=b7f43715b5e536d284571a7a506aaed9942b3a7d;hp=1059a888949cd21984837de1af31683fba90695f;hb=1ea2c3be33f2eb4630c5cdb78edf2bb670b294ab;hpb=0210f16cf1bd2f87b7fe4336311c6dfd88030f8b diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c index 1059a88..b7f4371 100644 --- a/utils/statd/sm-notify.c +++ b/utils/statd/sm-notify.c @@ -4,6 +4,11 @@ * Copyright (C) 2004-2006 Olaf Kirch */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include #include #include #include @@ -24,81 +29,329 @@ #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" - -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; +#include "sockaddr.h" +#include "xlog.h" +#include "nsm.h" +#include "nfsrpc.h" -#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 - -typedef struct sockaddr_storage nsm_address; struct nsm_host { struct nsm_host * next; char * name; - char * path; - nsm_address 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_host(int, struct nsm_host *); +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 *); -struct nsm_host * find_host(uint32_t); -static int addr_get_port(nsm_address *); -static void addr_set_port(nsm_address *, int); -static struct addrinfo *host_lookup(int, const char *); -void nsm_log(int fac, const char *fmt, ...); -static int record_pid(); -static void drop_privs(void); -static void set_kernel_nsm_state(int state); +static struct nsm_host *find_host(uint32_t); +static int record_pid(void); static struct nsm_host * hosts = NULL; +__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; + + 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) +{ + 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; + } + + 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_forget_host(struct nsm_host *host) +{ + 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 */ + +/* + * 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 = NULL; + struct addrinfo hint = { + .ai_flags = AI_NUMERICSERV, + .ai_family = nsm_family, + .ai_protocol = (int)IPPROTO_UDP, + }; + int error; + + if (srcaddr == NULL) + hint.ai_flags |= AI_PASSIVE; + + 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; - while ((c = getopt(argc, argv, "dm:np:v:qP:f")) != -1) { + progname = strrchr(argv[0], '/'); + if (progname != NULL) + progname++; + else + progname = argv[0]; + + while ((c = getopt(argc, argv, "dm:np:v:P:f")) != -1) { switch (c) { case 'f': force = 1; @@ -110,32 +363,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_WARNING, "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: @@ -145,43 +383,76 @@ 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"); - return 1; + "Usage: %s -notify [-dfq] [-m max-retry-minutes] [-p srcport]\n" + " [-P /path/to/state/directory] [-v my_host_name]\n", + progname); + exit(1); } - if (strcmp(_SM_BASE_PATH, BASEDIR) == 0) { - if (record_pid() == 0 && force == 0 && opt_update_state == 1) + 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 (nsm_is_default_parentdir()) { + if (record_pid() == 0 && force == 0 && opt_update_state) { /* already run, don't try again */ + 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) { - perror("gethostname"); - return 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); + (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"); - - openlog("sm-notify", LOG_PID, LOG_DAEMON); - log_syslog = 1; + xlog(L_NOTICE, "Backgrounding to notify hosts...\n"); if (daemon(0, 0) < 0) { - nsm_log(LOG_WARNING, "unable to background: %s", - strerror(errno)); - return 1; + xlog(L_ERROR, "unable to background: %m"); + exit(1); } close(0); @@ -189,83 +460,40 @@ 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); } - return 1; + exit(1); } - return 0; + exit(0); } /* * Notify hosts */ -void -notify(void) +static void +notify(const int sock) { - nsm_address local_addr; time_t failtime = 0; - int sock = -1; - int retry_cnt = 0; - - retry: - sock = socket(AF_INET, SOCK_DGRAM, 0); - if (sock < 0) { - perror("socket"); - exit(1); - } - fcntl(sock, F_SETFL, O_NONBLOCK); - - memset(&local_addr, 0, sizeof(local_addr)); - local_addr.ss_family = AF_INET; /* Default to IPv4 */ - - /* Bind source IP if provided on command line */ - if (opt_srcaddr) { - struct addrinfo *ai = host_lookup(AF_INET, opt_srcaddr); - if (!ai) { - nsm_log(LOG_WARNING, - "Not a valid hostname or address: \"%s\"\n", - opt_srcaddr); - exit(1); - } - memcpy(&local_addr, ai->ai_addr, ai->ai_addrlen); - /* We know it's IPv4 at this point */ - } - - /* Use source port if provided on the command line, - * otherwise use bindresvport */ - if (opt_srcport) { - addr_set_port(&local_addr, opt_srcport); - if (bind(sock, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0) { - perror("bind"); - exit(1); - } - } else { - struct servent *se; - (void) bindresvport(sock, (struct sockaddr_in *) &local_addr); - /* try to avoid known ports */ - se = getservbyport(local_addr.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); @@ -276,7 +504,7 @@ notify(void) if (failtime && now >= failtime) break; - while ((wait = hosts->send_next - now) <= 0) { + while (hosts && ((wait = hosts->send_next - now) <= 0)) { /* Never send more than 10 packets at once */ if (sent++ >= 10) break; @@ -285,7 +513,8 @@ notify(void) hp = hosts; hosts = hp->next; - notify_host(sock, hp); + if (notify_host(sock, hp)) + continue; /* Set the timeout for this call, using an exponential timeout strategy */ @@ -297,8 +526,10 @@ notify(void) insert_host(hp); } + 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; @@ -317,24 +548,22 @@ notify(void) /* * Send notification to a single host */ -void +static int notify_host(int sock, struct nsm_host *host) { - static unsigned int xid = 0; - nsm_address dest; - uint32_t msgbuf[MAXMSGSIZE], *p; - unsigned int len; - - if (!xid) - xid = getpid() + time(NULL); - if (!host->xid) - host->xid = xid++; - - memset(msgbuf, 0, sizeof(msgbuf)); - p = msgbuf; - *p++ = htonl(host->xid); - *p++ = 0; - *p++ = htonl(2); + 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(host->name); + if (host->ai == NULL) { + xlog_warn("DNS resolution of %s failed; " + "retrying later", host->name); + return 0; + } + } /* If we retransmitted 4 times, reset the port to force * a new portmap lookup (in case statd was restarted). @@ -342,223 +571,133 @@ notify_host(int sock, struct nsm_host *host) * point. */ if (host->retries >= 4) { - struct addrinfo *hold = host->ai; - struct addrinfo **next = &host->ai; - *next = hold->ai_next; - while ( *next ) - next = & (*next)->ai_next; - *next = hold; - hold->ai_next = NULL; - memcpy(&host->addr, hold->ai_addr, hold->ai_addrlen); - addr_set_port(&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; } - dest = host->addr; - if (addr_get_port(&dest) == 0) { - /* Build a PMAP packet */ - nsm_log(LOG_DEBUG, "Sending portmap query to %s", host->name); - - addr_set_port(&dest, 111); - *p++ = htonl(100000); - *p++ = htonl(2); - *p++ = htonl(3); + sap = host->ai->ai_addr; + salen = host->ai->ai_addrlen; - /* Auth and verf */ - *p++ = 0; *p++ = 0; - *p++ = 0; *p++ = 0; + 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); - *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; - - sendto(sock, msgbuf, len, 0, (struct sockaddr *) &dest, sizeof(dest)); + return 0; } /* - * Receive reply from remote host + * Extract the returned port number and set up the SM_NOTIFY call. */ -void -recv_reply(int sock) +static void +recv_rpcbind_reply(struct sockaddr *sap, struct nsm_host *host, XDR *xdr) { - struct nsm_host *hp; - 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..."); - - 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; + host->send_next = time(NULL); + host->xid = 0; - if (addr_get_port(&hp->addr) == 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 { - addr_set_port(&hp->addr, 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))) { - perror(dirname); - 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 names + * 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))) { - perror(dirname); + 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)); + 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; - - host->ai = host_lookup(AF_UNSPEC, de->d_name); - if (! host->ai) { - nsm_log(LOG_WARNING, - "%s doesn't seem to be a valid address, skipped", - de->d_name); - unlink(path); - continue; - } - - 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 */ + /* Before we look at the data, find the host struct for + this reply */ + if ((hp = find_host(xid)) == NULL) + goto out; - insert_host(host); - host = NULL; - } - closedir(dir); + sap = hp->ai->ai_addr; + if (nfs_get_port(sap) == 0) + recv_rpcbind_reply(sap, hp, &xdr); + else + recv_notify_reply(hp); - if (host) - free(host); +out: + xdr_destroy(&xdr); } /* * Insert host into sorted list */ -void +static void insert_host(struct nsm_host *host) { struct nsm_host **where, *p; @@ -586,7 +725,7 @@ insert_host(struct nsm_host *host) /* * Find host given the XID */ -struct nsm_host * +static struct nsm_host * find_host(uint32_t xid) { struct nsm_host **where, *p; @@ -602,184 +741,29 @@ find_host(uint32_t xid) return NULL; } - -/* - * Retrieve the current NSM state - */ -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_WARNING, "Cannot create %s: %m", newfile); - exit(1); - } - if (write(fd, &state, sizeof(state)) != sizeof(state)) { - nsm_log(LOG_WARNING, - "Failed to write state to %s", newfile); - exit(1); - } - close(fd); - if (rename(newfile, _SM_STATE_PATH) < 0) { - nsm_log(LOG_WARNING, - "Cannot create %s: %m", _SM_STATE_PATH); - exit(1); - } - sync(); - } - - return state; -} - -/* - * Address handling utilities - */ - -int -addr_get_port(nsm_address *addr) -{ - switch (((struct sockaddr *) addr)->sa_family) { - case AF_INET: - return ntohs(((struct sockaddr_in *) addr)->sin_port); - case AF_INET6: - return ntohs(((struct sockaddr_in6 *) addr)->sin6_port); - } - return 0; -} - -static void -addr_set_port(nsm_address *addr, int port) -{ - switch (((struct sockaddr *) addr)->sa_family) { - case AF_INET: - ((struct sockaddr_in *) addr)->sin_port = htons(port); - break; - case AF_INET6: - ((struct sockaddr_in6 *) addr)->sin6_port = htons(port); - } -} - -static struct addrinfo * -host_lookup(int af, const char *name) -{ - struct addrinfo hints, *ai; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = af; - hints.ai_protocol = IPPROTO_UDP; - - if (getaddrinfo(name, NULL, &hints, &ai) != 0) - return NULL; - - return ai; -} - -/* - * Log a message - */ -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 * program exits. * If file already exists, fail. */ -static int record_pid() +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; + 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); } - if (st.st_uid == 0) { - nsm_log(LOG_WARNING, - "sm-notify running as root. chown %s to choose different user\n", - _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); - } -} - -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; }