From 589c0304bd0122c1084a3f4d8b3d4b53fc919232 Mon Sep 17 00:00:00 2001 From: Steve Dickson Date: Jan 12 2010 12:24:47 +0000 Subject: Updated to latest upstream RC release: nfs-utils-1-2-2-rc5 --- diff --git a/nfs-utils-1.2.2-rc5.patch b/nfs-utils-1.2.2-rc5.patch new file mode 100644 index 0000000..9b67b1f --- /dev/null +++ b/nfs-utils-1.2.2-rc5.patch @@ -0,0 +1,6163 @@ +diff -up nfs-utils-1.2.1/configure.ac.orig nfs-utils-1.2.1/configure.ac +--- nfs-utils-1.2.1/configure.ac.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/configure.ac 2010-01-12 06:07:40.754815941 -0500 +@@ -402,6 +402,7 @@ AC_CONFIG_FILES([ + support/include/Makefile + support/misc/Makefile + support/nfs/Makefile ++ support/nsm/Makefile + tools/Makefile + tools/locktest/Makefile + tools/nlmtest/Makefile +@@ -416,6 +417,8 @@ AC_CONFIG_FILES([ + utils/nfsd/Makefile + utils/nfsstat/Makefile + utils/showmount/Makefile +- utils/statd/Makefile]) ++ utils/statd/Makefile ++ tests/Makefile ++ tests/nsm_client/Makefile]) + AC_OUTPUT + +diff -up nfs-utils-1.2.1/.gitignore.orig nfs-utils-1.2.1/.gitignore +--- nfs-utils-1.2.1/.gitignore.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/.gitignore 2010-01-12 06:07:40.753815840 -0500 +@@ -55,10 +55,15 @@ support/export/mount.h + support/export/mount_clnt.c + support/export/mount_xdr.c + support/include/mount.h +-utils/statd/sm_inter.h +-utils/statd/sm_inter_clnt.c +-utils/statd/sm_inter_svc.c +-utils/statd/sm_inter_xdr.c ++support/nsm/sm_inter.h ++support/nsm/sm_inter_clnt.c ++support/nsm/sm_inter_svc.c ++support/nsm/sm_inter_xdr.c ++support/include/sm_inter.h ++tests/nsm_client/nlm_sm_inter.h ++tests/nsm_client/nlm_sm_inter_clnt.c ++tests/nsm_client/nlm_sm_inter_svc.c ++tests/nsm_client/nlm_sm_inter_xdr.c + # cscope database files + cscope.* + # generic editor backup et al +diff -up nfs-utils-1.2.1/Makefile.am.orig nfs-utils-1.2.1/Makefile.am +--- nfs-utils-1.2.1/Makefile.am.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/Makefile.am 2010-01-12 06:07:40.753815840 -0500 +@@ -2,7 +2,7 @@ + + AUTOMAKE_OPTIONS = foreign + +-SUBDIRS = tools support utils linux-nfs ++SUBDIRS = tools support utils linux-nfs tests + + MAINTAINERCLEANFILES = Makefile.in + +diff -up nfs-utils-1.2.1/support/export/client.c.orig nfs-utils-1.2.1/support/export/client.c +--- nfs-utils-1.2.1/support/export/client.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/support/export/client.c 2010-01-12 06:07:40.755815903 -0500 +@@ -297,7 +297,7 @@ name_cmp(char *a, char *b) + /* compare strings a and b, but only upto ',' in a */ + while (*a && *b && *a != ',' && *a == *b) + a++, b++; +- if (!*b && (!*a || !a == ',') ) ++ if (!*b && (!*a || *a == ',')) + return 0; + if (!*b) return 1; + if (!*a || *a == ',') return -1; +diff -up nfs-utils-1.2.1/support/include/ha-callout.h.orig nfs-utils-1.2.1/support/include/ha-callout.h +--- nfs-utils-1.2.1/support/include/ha-callout.h.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/support/include/ha-callout.h 2010-01-12 06:07:40.756815715 -0500 +@@ -53,11 +53,7 @@ ha_callout(char *event, char *arg1, char + default: pid = waitpid(pid, &ret, 0); + } + sigaction(SIGCHLD, &oldact, &newact); +-#ifdef dprintf +- dprintf(N_DEBUG, "ha callout returned %d\n", WEXITSTATUS(ret)); +-#else + xlog(D_GENERAL, "ha callout returned %d\n", WEXITSTATUS(ret)); +-#endif + } + + #endif +diff -up nfs-utils-1.2.1/support/include/Makefile.am.orig nfs-utils-1.2.1/support/include/Makefile.am +--- nfs-utils-1.2.1/support/include/Makefile.am.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/support/include/Makefile.am 2010-01-12 06:07:40.756815715 -0500 +@@ -9,6 +9,8 @@ noinst_HEADERS = \ + nfs_mntent.h \ + nfs_paths.h \ + nfslib.h \ ++ nfsrpc.h \ ++ nsm.h \ + rpcmisc.h \ + tcpwrapper.h \ + xio.h \ +diff -up nfs-utils-1.2.1/support/include/nfsrpc.h.orig nfs-utils-1.2.1/support/include/nfsrpc.h +--- nfs-utils-1.2.1/support/include/nfsrpc.h.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/support/include/nfsrpc.h 2010-01-12 06:07:40.757815668 -0500 +@@ -90,6 +90,18 @@ extern CLIENT *nfs_get_priv_rpcclient( + struct timeval *); + + /* ++ * Convert a netid to a protocol number and protocol family ++ */ ++extern int nfs_get_proto(const char *netid, sa_family_t *family, ++ unsigned long *protocol); ++ ++/* ++ * Convert a protocol family and protocol name to a netid ++ */ ++extern char *nfs_get_netid(const sa_family_t family, ++ const unsigned long protocol); ++ ++/* + * Convert a socket address to a universal address + */ + extern char *nfs_sockaddr2universal(const struct sockaddr *); +diff -up nfs-utils-1.2.1/support/include/nsm.h.orig nfs-utils-1.2.1/support/include/nsm.h +--- nfs-utils-1.2.1/support/include/nsm.h.orig 2010-01-12 06:07:40.757815668 -0500 ++++ nfs-utils-1.2.1/support/include/nsm.h 2010-01-12 06:07:40.758815846 -0500 +@@ -0,0 +1,66 @@ ++/* ++ * Copyright 2009 Oracle. All rights reserved. ++ * ++ * This file is part of nfs-utils. ++ * ++ * nfs-utils is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * nfs-utils is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with nfs-utils. If not, see . ++ */ ++ ++/* ++ * NSM for Linux. ++ */ ++ ++#ifndef NFS_UTILS_SUPPORT_NSM_H ++#define NFS_UTILS_SUPPORT_NSM_H ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "sm_inter.h" ++ ++typedef unsigned int ++ (*nsm_populate_t)(const char *hostname, ++ const struct sockaddr *sap, ++ const struct mon *mon, ++ const time_t timestamp); ++ ++/* file.c */ ++ ++extern _Bool nsm_setup_pathnames(const char *progname, ++ const char *parentdir); ++extern _Bool nsm_is_default_parentdir(void); ++extern _Bool nsm_drop_privileges(const int pidfd); ++ ++extern int nsm_get_state(_Bool update); ++extern void nsm_update_kernel_state(const int state); ++ ++extern unsigned int ++ nsm_retire_monitored_hosts(void); ++extern unsigned int ++ nsm_load_monitor_list(nsm_populate_t func); ++extern unsigned int ++ nsm_load_notify_list(nsm_populate_t func); ++ ++extern _Bool nsm_insert_monitored_host(const char *hostname, ++ const struct sockaddr *sap, const struct mon *m); ++extern void nsm_delete_monitored_host(const char *hostname); ++extern void nsm_delete_notified_host(const char *hostname); ++extern size_t nsm_priv_to_hex(const char *priv, char *buf, ++ const size_t buflen); ++ ++#endif /* !NFS_UTILS_SUPPORT_NSM_H */ +diff -up nfs-utils-1.2.1/support/Makefile.am.orig nfs-utils-1.2.1/support/Makefile.am +--- nfs-utils-1.2.1/support/Makefile.am.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/support/Makefile.am 2010-01-12 06:07:40.755815903 -0500 +@@ -1,6 +1,6 @@ + ## Process this file with automake to produce Makefile.in + +-SUBDIRS = export include misc nfs ++SUBDIRS = export include misc nfs nsm + + MAINTAINERCLEANFILES = Makefile.in + +diff -up nfs-utils-1.2.1/support/nfs/getport.c.orig nfs-utils-1.2.1/support/nfs/getport.c +--- nfs-utils-1.2.1/support/nfs/getport.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/support/nfs/getport.c 2010-01-12 06:07:40.758815846 -0500 +@@ -199,7 +199,63 @@ static CLIENT *nfs_gp_get_rpcbclient(str + return clnt; + } + +-/* ++/** ++ * nfs_get_proto - Convert a netid to an address family and protocol number ++ * @netid: C string containing a netid ++ * @family: OUT: address family ++ * @protocol: OUT: protocol number ++ * ++ * Returns 1 and fills in @protocol if the netid was recognized; ++ * otherwise zero is returned. ++ */ ++#ifdef HAVE_LIBTIRPC ++int ++nfs_get_proto(const char *netid, sa_family_t *family, unsigned long *protocol) ++{ ++ struct netconfig *nconf; ++ struct protoent *proto; ++ ++ nconf = getnetconfigent(netid); ++ if (nconf == NULL) ++ return 0; ++ ++ proto = getprotobyname(nconf->nc_proto); ++ if (proto == NULL) { ++ freenetconfigent(nconf); ++ return 0; ++ } ++ ++ *family = AF_UNSPEC; ++ if (strcmp(nconf->nc_protofmly, NC_INET) == 0) ++ *family = AF_INET; ++ if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) ++ *family = AF_INET6; ++ freenetconfigent(nconf); ++ ++ *protocol = (unsigned long)proto->p_proto; ++ return 1; ++} ++#else /* !HAVE_LIBTIRPC */ ++int ++nfs_get_proto(const char *netid, sa_family_t *family, unsigned long *protocol) ++{ ++ struct protoent *proto; ++ ++ proto = getprotobyname(netid); ++ if (proto == NULL) ++ return 0; ++ ++ *family = AF_INET; ++ *protocol = (unsigned long)proto->p_proto; ++ return 1; ++} ++#endif /* !HAVE_LIBTIRPC */ ++ ++/** ++ * nfs_get_netid - Convert a protocol family and protocol name to a netid ++ * @family: protocol family ++ * @protocol: protocol number ++ * + * One of the arguments passed when querying remote rpcbind services + * via rpcbind v3 or v4 is a netid string. This replaces the pm_prot + * field used in legacy PMAP_GETPORT calls. +@@ -213,13 +269,12 @@ static CLIENT *nfs_gp_get_rpcbclient(str + * first entry that matches @family and @protocol and whose netid string + * fits in the provided buffer. + * +- * Returns a '\0'-terminated string if successful; otherwise NULL. ++ * Returns a '\0'-terminated string if successful. Caller must ++ * free the returned string. Otherwise NULL is returned, and + * rpc_createerr.cf_stat is set to reflect the error. + */ + #ifdef HAVE_LIBTIRPC +- +-static char *nfs_gp_get_netid(const sa_family_t family, +- const unsigned short protocol) ++char *nfs_get_netid(const sa_family_t family, const unsigned long protocol) + { + char *nc_protofmly, *nc_proto, *nc_netid; + struct netconfig *nconf; +@@ -255,6 +310,9 @@ static char *nfs_gp_get_netid(const sa_f + + nc_netid = strdup(nconf->nc_netid); + endnetconfig(handle); ++ ++ if (nc_netid == NULL) ++ rpc_createerr.cf_stat = RPC_SYSTEMERROR; + return nc_netid; + } + endnetconfig(handle); +@@ -263,8 +321,28 @@ out: + rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; + return NULL; + } ++#else /* !HAVE_LIBTIRPC */ ++char *nfs_get_netid(const sa_family_t family, const unsigned long protocol) ++{ ++ struct protoent *proto; ++ char *netid; + +-#endif /* HAVE_LIBTIRPC */ ++ if (family != AF_INET) ++ goto out; ++ proto = getprotobynumber((int)protocol); ++ if (proto == NULL) ++ goto out; ++ ++ netid = strdup(proto->p_name); ++ if (netid == NULL) ++ rpc_createerr.cf_stat = RPC_SYSTEMERROR; ++ return netid; ++ ++out: ++ rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; ++ return NULL; ++} ++#endif /* !HAVE_LIBTIRPC */ + + /* + * Extract a port number from a universal address, and terminate the +@@ -421,7 +499,7 @@ static int nfs_gp_init_rpcb_parms(const + { + char *netid, *addr; + +- netid = nfs_gp_get_netid(sap->sa_family, protocol); ++ netid = nfs_get_netid(sap->sa_family, protocol); + if (netid == NULL) + return 0; + +diff -up nfs-utils-1.2.1/support/nsm/file.c.orig nfs-utils-1.2.1/support/nsm/file.c +--- nfs-utils-1.2.1/support/nsm/file.c.orig 2010-01-12 06:07:40.760815569 -0500 ++++ nfs-utils-1.2.1/support/nsm/file.c 2010-01-12 06:07:40.760815569 -0500 +@@ -0,0 +1,843 @@ ++/* ++ * Copyright 2009 Oracle. All rights reserved. ++ * ++ * This file is part of nfs-utils. ++ * ++ * nfs-utils is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * nfs-utils is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with nfs-utils. If not, see . ++ */ ++ ++/* ++ * NSM for Linux. ++ * ++ * Callback information and NSM state is stored in files, usually ++ * under /var/lib/nfs. A database of information contained in local ++ * files stores NLM callback data and what remote peers to notify of ++ * reboots. ++ * ++ * For each monitored remote peer, a text file is created under the ++ * directory specified by NSM_MONITOR_DIR. The name of the file ++ * is a valid DNS hostname. The hostname string must be a valid ++ * ASCII DNS name, and must not contain slash characters, white space, ++ * or '\0' (ie. anything that might have some special meaning in a ++ * path name). ++ * ++ * The contents of each file include seven blank-separated fields of ++ * text, finished with '\n'. The first field contains the network ++ * address of the NLM service to call back. The current implementation ++ * supports using only IPv4 addresses, so the only contents of this ++ * field are a network order IPv4 address expressed in 8 hexadecimal ++ * characters. ++ * ++ * The next four fields are text strings of hexadecimal characters, ++ * representing: ++ * ++ * 2. A 4 byte RPC program number of the NLM service to call back ++ * 3. A 4 byte RPC version number of the NLM service to call back ++ * 4. A 4 byte RPC procedure number of the NLM service to call back ++ * 5. A 16 byte opaque cookie that the NLM service uses to identify ++ * the monitored host ++ * ++ * The sixth field is the monitored host's mon_name, passed to statd ++ * via an SM_MON request. ++ * ++ * The seventh field is the my_name for this peer, which is the ++ * hostname of the local NLM (currently on Linux, the result of ++ * `uname -n`). This can be used as the source address/hostname ++ * when sending SM_NOTIFY requests. ++ * ++ * The NSM protocol does not limit the contents of these strings ++ * in any way except that they must fit into 1024 bytes. Our ++ * implementation requires that these strings not contain ++ * white space or '\0'. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include ++#include ++ ++#include ++#include ++#include ++#ifndef S_SPLINT_S ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "xlog.h" ++#include "nsm.h" ++ ++#define RPCARGSLEN (4 * (8 + 1)) ++#define LINELEN (RPCARGSLEN + SM_PRIV_SIZE * 2 + 1) ++ ++#define NSM_KERNEL_STATE_FILE "/proc/sys/fs/nfs/nsm_local_state" ++ ++/* ++ * Some distributions place statd's files in a subdirectory ++ */ ++#define NSM_PATH_EXTENSION ++/* #define NSM_PATH_EXTENSION "/statd" */ ++ ++#define NSM_DEFAULT_STATEDIR NFS_STATEDIR NSM_PATH_EXTENSION ++ ++static char nsm_base_dirname[PATH_MAX] = NSM_DEFAULT_STATEDIR; ++ ++#define NSM_MONITOR_DIR "sm" ++#define NSM_NOTIFY_DIR "sm.bak" ++#define NSM_STATE_FILE "state" ++ ++ ++static _Bool ++error_check(const int len, const size_t buflen) ++{ ++ return (len < 0) || ((size_t)len >= buflen); ++} ++ ++static _Bool ++exact_error_check(const ssize_t len, const size_t buflen) ++{ ++ return (len < 0) || ((size_t)len != buflen); ++} ++ ++/* ++ * Returns a dynamically allocated, '\0'-terminated buffer ++ * containing an appropriate pathname, or NULL if an error ++ * occurs. Caller must free the returned result with free(3). ++ */ ++__attribute_malloc__ ++static char * ++nsm_make_record_pathname(const char *directory, const char *hostname) ++{ ++ const char *c; ++ size_t size; ++ char *path; ++ int len; ++ ++ /* ++ * Block hostnames that contain characters that have ++ * meaning to the file system (like '/'), or that can ++ * be confusing on visual inspection (like ' '). ++ */ ++ for (c = hostname; *c != '\0'; c++) ++ if (*c == '/' || isspace((int)*c) != 0) { ++ xlog(D_GENERAL, "Hostname contains invalid characters"); ++ return NULL; ++ } ++ ++ size = strlen(nsm_base_dirname) + strlen(directory) + strlen(hostname) + 3; ++ if (size > PATH_MAX) { ++ xlog(D_GENERAL, "Hostname results in pathname that is too long"); ++ return NULL; ++ } ++ ++ path = malloc(size); ++ if (path == NULL) { ++ xlog(D_GENERAL, "Failed to allocate memory for pathname"); ++ return NULL; ++ } ++ ++ len = snprintf(path, size, "%s/%s/%s", ++ nsm_base_dirname, directory, hostname); ++ if (error_check(len, size)) { ++ xlog(D_GENERAL, "Pathname did not fit in specified buffer"); ++ free(path); ++ return NULL; ++ } ++ ++ return path; ++} ++ ++/* ++ * Returns a dynamically allocated, '\0'-terminated buffer ++ * containing an appropriate pathname, or NULL if an error ++ * occurs. Caller must free the returned result with free(3). ++ */ ++__attribute_malloc__ ++static char * ++nsm_make_pathname(const char *directory) ++{ ++ size_t size; ++ char *path; ++ int len; ++ ++ size = strlen(nsm_base_dirname) + strlen(directory) + 2; ++ if (size > PATH_MAX) ++ return NULL; ++ ++ path = malloc(size); ++ if (path == NULL) ++ return NULL; ++ ++ len = snprintf(path, size, "%s/%s", nsm_base_dirname, directory); ++ if (error_check(len, size)) { ++ free(path); ++ return NULL; ++ } ++ ++ return path; ++} ++ ++/** ++ * nsm_setup_pathnames - set up pathname ++ * @progname: C string containing name of program, for error messages ++ * @parentdir: C string containing pathname to on-disk state, or NULL ++ * ++ * This runs before logging is set up, so error messages are directed ++ * to stderr. ++ * ++ * Returns true and sets up our pathnames, if @parentdir was valid ++ * and usable; otherwise false is returned. ++ */ ++_Bool ++nsm_setup_pathnames(const char *progname, const char *parentdir) ++{ ++ static char buf[PATH_MAX]; ++ struct stat st; ++ char *path; ++ ++ /* First: test length of name and whether it exists */ ++ if (lstat(parentdir, &st) == -1) { ++ (void)fprintf(stderr, "%s: Failed to stat %s: %s", ++ progname, parentdir, strerror(errno)); ++ return false; ++ } ++ ++ /* Ensure we have a clean directory pathname */ ++ strncpy(buf, parentdir, sizeof(buf)); ++ path = dirname(buf); ++ if (*path == '.') { ++ (void)fprintf(stderr, "%s: Unusable directory %s", ++ progname, parentdir); ++ return false; ++ } ++ ++ xlog(D_CALL, "Using %s as the state directory", parentdir); ++ strncpy(nsm_base_dirname, parentdir, sizeof(nsm_base_dirname)); ++ return true; ++} ++ ++/** ++ * nsm_is_default_parentdir - check if parent directory is default ++ * ++ * Returns true if the active statd parent directory, set by ++ * nsm_change_pathname(), is the same as the built-in default ++ * parent directory; otherwise false is returned. ++ */ ++_Bool ++nsm_is_default_parentdir(void) ++{ ++ return strcmp(nsm_base_dirname, NSM_DEFAULT_STATEDIR) == 0; ++} ++ ++/** ++ * nsm_drop_privileges - drop root privileges ++ * @pidfd: file descriptor of a pid file ++ * ++ * Returns true if successful, or false if some error occurred. ++ * ++ * Set our effective UID and GID to that of our on-disk database. ++ */ ++_Bool ++nsm_drop_privileges(const int pidfd) ++{ ++ struct stat st; ++ ++ (void)umask(S_IRWXO); ++ ++ /* ++ * XXX: If we can't stat dirname, or if dirname is owned by ++ * root, we should use "statduser" instead, which is set up ++ * by configure.ac. Nothing in nfs-utils seems to use ++ * "statduser," though. ++ */ ++ if (lstat(nsm_base_dirname, &st) == -1) { ++ xlog(L_ERROR, "Failed to stat %s: %m", nsm_base_dirname); ++ return false; ++ } ++ ++ if (st.st_uid == 0) { ++ xlog_warn("Running as root. " ++ "chown %s to choose different user", nsm_base_dirname); ++ return true; ++ } ++ ++ if (chdir(nsm_base_dirname) == -1) { ++ xlog(L_ERROR, "Failed to change working directory to %s: %m", ++ nsm_base_dirname); ++ return false; ++ } ++ ++ /* ++ * If the pidfile happens to reside on NFS, dropping privileges ++ * will probably cause us to lose access, even though we are ++ * holding it open. Chown it to prevent this. ++ */ ++ if (pidfd >= 0) ++ if (fchown(pidfd, st.st_uid, st.st_gid) == -1) ++ xlog_warn("Failed to change owner of pidfile: %m"); ++ ++ if (setgroups(0, NULL) == -1) { ++ xlog(L_ERROR, "Failed to drop supplementary groups: %m"); ++ return false; ++ } ++ ++ /* ++ * ORDER ++ * ++ * setgid(2) first, as setuid(2) may remove privileges needed ++ * to set the group id. ++ */ ++ if (setgid(st.st_gid) == -1 || setuid(st.st_uid) == -1) { ++ xlog(L_ERROR, "Failed to drop privileges: %m"); ++ return false; ++ } ++ ++ xlog(D_CALL, "Effective UID, GID: %u, %u", st.st_uid, st.st_gid); ++ return true; ++} ++ ++/** ++ * nsm_get_state - retrieve on-disk NSM state number ++ * ++ * Returns an odd NSM state number read from disk, or an initial ++ * state number. Zero is returned if some error occurs. ++ */ ++int ++nsm_get_state(_Bool update) ++{ ++ int fd, state = 0; ++ ssize_t result; ++ char *path = NULL; ++ char *newpath = NULL; ++ ++ path = nsm_make_pathname(NSM_STATE_FILE); ++ if (path == NULL) { ++ xlog(L_ERROR, "Failed to allocate path for " NSM_STATE_FILE); ++ goto out; ++ } ++ ++ fd = open(path, O_RDONLY); ++ if (fd == -1) { ++ if (errno != ENOENT) { ++ xlog(L_ERROR, "Failed to open %s: %m", path); ++ goto out; ++ } ++ ++ xlog(L_NOTICE, "Initializing NSM state"); ++ state = 1; ++ update = true; ++ goto update; ++ } ++ ++ result = read(fd, &state, sizeof(state)); ++ if (exact_error_check(result, sizeof(state))) { ++ xlog_warn("Failed to read %s: %m", path); ++ ++ xlog(L_NOTICE, "Initializing NSM state"); ++ state = 1; ++ update = true; ++ goto update; ++ } ++ ++ if ((state & 1) == 0) ++ state++; ++ ++update: ++ (void)close(fd); ++ ++ if (update) { ++ state += 2; ++ ++ newpath = nsm_make_pathname(NSM_STATE_FILE ".new"); ++ if (newpath == NULL) { ++ xlog(L_ERROR, ++ "Failed to create path for " NSM_STATE_FILE ".new"); ++ state = 0; ++ goto out; ++ } ++ ++ fd = open(newpath, O_CREAT | O_SYNC | O_WRONLY, 0644); ++ if (fd == -1) { ++ xlog(L_ERROR, "Failed to create %s: %m", newpath); ++ state = 0; ++ goto out; ++ } ++ ++ result = write(fd, &state, sizeof(state)); ++ if (exact_error_check(result, sizeof(state))) { ++ xlog(L_ERROR, "Failed to write %s: %m", newpath); ++ (void)close(fd); ++ (void)unlink(newpath); ++ state = 0; ++ goto out; ++ } ++ ++ if (close(fd) == -1) { ++ xlog(L_ERROR, "Failed to close %s: %m", newpath); ++ (void)unlink(newpath); ++ state = 0; ++ goto out; ++ } ++ ++ if (rename(newpath, path) == -1) { ++ xlog(L_ERROR, "Failed to rename %s -> %s: %m", ++ newpath, path); ++ (void)unlink(newpath); ++ state = 0; ++ goto out; ++ } ++ ++ /* Ostensibly, a sync(2) is not needed here because ++ * open(O_CREAT), write(O_SYNC), and rename(2) are ++ * already synchronous with persistent storage, for ++ * any file system we care about. */ ++ } ++ ++out: ++ free(newpath); ++ free(path); ++ return state; ++} ++ ++/** ++ * nsm_update_kernel_state - attempt to post new NSM state to kernel ++ * @state: NSM state number ++ * ++ */ ++void ++nsm_update_kernel_state(const int state) ++{ ++ ssize_t result; ++ char buf[20]; ++ int fd, len; ++ ++ fd = open(NSM_KERNEL_STATE_FILE, O_WRONLY); ++ if (fd == -1) { ++ xlog(D_GENERAL, "Failed to open " NSM_KERNEL_STATE_FILE ": %m"); ++ return; ++ } ++ ++ len = snprintf(buf, sizeof(buf), "%d", state); ++ if (error_check(len, sizeof(buf))) { ++ xlog_warn("Failed to form NSM state number string"); ++ return; ++ } ++ ++ result = write(fd, buf, strlen(buf)); ++ if (exact_error_check(result, strlen(buf))) ++ xlog_warn("Failed to write NSM state number: %m"); ++ ++ if (close(fd) == -1) ++ xlog(L_ERROR, "Failed to close NSM state file " ++ NSM_KERNEL_STATE_FILE ": %m"); ++} ++ ++/** ++ * nsm_retire_monitored_hosts - back up all hosts from "sm/" to "sm.bak/" ++ * ++ * Returns the count of host records that were moved. ++ * ++ * Note that if any error occurs during this process, some monitor ++ * records may be left in the "sm" directory. ++ */ ++unsigned int ++nsm_retire_monitored_hosts(void) ++{ ++ unsigned int count = 0; ++ struct dirent *de; ++ char *path; ++ DIR *dir; ++ ++ path = nsm_make_pathname(NSM_MONITOR_DIR); ++ if (path == NULL) { ++ xlog(L_ERROR, "Failed to allocate path for " NSM_MONITOR_DIR); ++ return count; ++ } ++ ++ dir = opendir(path); ++ free(path); ++ if (dir == NULL) { ++ xlog_warn("Failed to open " NSM_MONITOR_DIR ": %m"); ++ return count; ++ } ++ ++ while ((de = readdir(dir)) != NULL) { ++ char *src, *dst; ++ ++ if (de->d_type != (unsigned char)DT_REG) ++ continue; ++ if (de->d_name[0] == '.') ++ continue; ++ ++ src = nsm_make_record_pathname(NSM_MONITOR_DIR, de->d_name); ++ if (src == NULL) { ++ xlog_warn("Bad monitor file name, skipping"); ++ continue; ++ } ++ ++ dst = nsm_make_record_pathname(NSM_NOTIFY_DIR, de->d_name); ++ if (dst == NULL) { ++ free(src); ++ xlog_warn("Bad notify file name, skipping"); ++ continue; ++ } ++ ++ if (rename(src, dst) == -1) ++ xlog_warn("Failed to rename %s -> %s: %m", ++ src, dst); ++ else { ++ xlog(D_GENERAL, "Retired record for mon_name %s", ++ de->d_name); ++ count++; ++ } ++ ++ free(dst); ++ free(src); ++ } ++ ++ (void)closedir(dir); ++ return count; ++} ++ ++/* ++ * nsm_priv_to_hex - convert a NSM private cookie to a hex string. ++ * ++ * @priv: buffer holding the binary NSM private cookie ++ * @buf: output buffer for NULL terminated hex string ++ * @buflen: size of output buffer ++ * ++ * Returns the length of the resulting string or 0 on error ++ */ ++size_t ++nsm_priv_to_hex(const char *priv, char *buf, const size_t buflen) ++{ ++ int i, len; ++ size_t remaining = buflen; ++ ++ for (i = 0; i < SM_PRIV_SIZE; i++) { ++ len = snprintf(buf, remaining, "%02x", ++ (unsigned int)(0xff & priv[i])); ++ if (error_check(len, remaining)) ++ return 0; ++ buf += len; ++ remaining -= (size_t)len; ++ } ++ ++ return buflen - remaining; ++} ++ ++/* ++ * Returns the length in bytes of the created record. ++ */ ++__attribute_noinline__ ++static size_t ++nsm_create_monitor_record(char *buf, const size_t buflen, ++ const struct sockaddr *sap, const struct mon *m) ++{ ++ const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; ++ size_t hexlen, remaining = buflen; ++ int len; ++ ++ len = snprintf(buf, remaining, "%08x %08x %08x %08x ", ++ (unsigned int)sin->sin_addr.s_addr, ++ (unsigned int)m->mon_id.my_id.my_prog, ++ (unsigned int)m->mon_id.my_id.my_vers, ++ (unsigned int)m->mon_id.my_id.my_proc); ++ if (error_check(len, remaining)) ++ return 0; ++ buf += len; ++ remaining -= (size_t)len; ++ ++ hexlen = nsm_priv_to_hex(m->priv, buf, remaining); ++ if (hexlen == 0) ++ return 0; ++ buf += hexlen; ++ remaining -= hexlen; ++ ++ len = snprintf(buf, remaining, " %s %s\n", ++ m->mon_id.mon_name, m->mon_id.my_id.my_name); ++ if (error_check(len, remaining)) ++ return 0; ++ remaining -= (size_t)len; ++ ++ return buflen - remaining; ++} ++ ++/** ++ * nsm_insert_monitored_host - write callback data for one host to disk ++ * @hostname: C string containing a hostname ++ * @sap: sockaddr containing NLM callback address ++ * @mon: SM_MON arguments to save ++ * ++ * Returns true if successful, otherwise false if some error occurs. ++ */ ++_Bool ++nsm_insert_monitored_host(const char *hostname, const struct sockaddr *sap, ++ const struct mon *m) ++{ ++ static char buf[LINELEN + 1 + SM_MAXSTRLEN + 2]; ++ char *path; ++ _Bool result = false; ++ ssize_t len; ++ size_t size; ++ int fd; ++ ++ path = nsm_make_record_pathname(NSM_MONITOR_DIR, hostname); ++ if (path == NULL) { ++ xlog(L_ERROR, "Failed to insert: bad monitor hostname '%s'", ++ hostname); ++ return false; ++ } ++ ++ size = nsm_create_monitor_record(buf, sizeof(buf), sap, m); ++ if (size == 0) { ++ xlog(L_ERROR, "Failed to insert: record too long"); ++ goto out; ++ } ++ ++ fd = open(path, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR); ++ if (fd == -1) { ++ xlog(L_ERROR, "Failed to insert: creating %s: %m", path); ++ goto out; ++ } ++ result = true; ++ ++ len = write(fd, buf, size); ++ if (exact_error_check(len, size)) { ++ xlog_warn("Failed to insert: writing %s: %m", path); ++ (void)unlink(path); ++ result = false; ++ } ++ ++ if (close(fd) == -1) { ++ xlog(L_ERROR, "Failed to insert: closing %s: %m", path); ++ (void)unlink(path); ++ result = false; ++ } ++ ++out: ++ free(path); ++ return result; ++} ++ ++__attribute_noinline__ ++static _Bool ++nsm_parse_line(char *line, struct sockaddr_in *sin, struct mon *m) ++{ ++ unsigned int i, tmp; ++ int count; ++ char *c; ++ ++ c = strchr(line, '\n'); ++ if (c != NULL) ++ *c = '\0'; ++ ++ count = sscanf(line, "%8x %8x %8x %8x ", ++ (unsigned int *)&sin->sin_addr.s_addr, ++ (unsigned int *)&m->mon_id.my_id.my_prog, ++ (unsigned int *)&m->mon_id.my_id.my_vers, ++ (unsigned int *)&m->mon_id.my_id.my_proc); ++ if (count != 4) ++ return false; ++ ++ c = line + RPCARGSLEN; ++ for (i = 0; i < SM_PRIV_SIZE; i++) { ++ if (sscanf(c, "%2x", &tmp) != 1) ++ return false; ++ m->priv[i] = (char)tmp; ++ c += 2; ++ } ++ ++ c++; ++ m->mon_id.mon_name = c; ++ while (*c != '\0' && *c != ' ') ++ c++; ++ if (*c != '\0') ++ *c++ = '\0'; ++ while (*c == ' ') ++ c++; ++ m->mon_id.my_id.my_name = c; ++ ++ return true; ++} ++ ++/* ++ * Stuff a 'struct mon' with callback data, and call @func. ++ * ++ * Returns the count of in-core records created. ++ */ ++static unsigned int ++nsm_read_line(const char *hostname, const time_t timestamp, char *line, ++ nsm_populate_t func) ++{ ++ struct sockaddr_in sin = { ++ .sin_family = AF_INET, ++ }; ++ struct mon m; ++ ++ if (!nsm_parse_line(line, &sin, &m)) ++ return 0; ++ ++ return func(hostname, (struct sockaddr *)(char *)&sin, &m, timestamp); ++} ++ ++/* ++ * Given a filename, reads data from a file under NSM_MONITOR_DIR ++ * and invokes @func so caller can populate their in-core ++ * database with this data. ++ */ ++static unsigned int ++nsm_load_host(const char *directory, const char *filename, nsm_populate_t func) ++{ ++ char buf[LINELEN + 1 + SM_MAXSTRLEN + 2]; ++ unsigned int result = 0; ++ struct stat stb; ++ char *path; ++ FILE *f; ++ ++ path = nsm_make_record_pathname(directory, filename); ++ if (path == NULL) ++ goto out_err; ++ ++ if (stat(path, &stb) == -1) { ++ xlog(L_ERROR, "Failed to stat %s: %m", path); ++ goto out_freepath; ++ } ++ ++ f = fopen(path, "r"); ++ if (f == NULL) { ++ xlog(L_ERROR, "Failed to open %s: %m", path); ++ goto out_freepath; ++ } ++ ++ while (fgets(buf, (int)sizeof(buf), f) != NULL) { ++ buf[sizeof(buf) - 1] = '\0'; ++ result += nsm_read_line(filename, stb.st_mtime, buf, func); ++ } ++ if (result == 0) ++ xlog(L_ERROR, "Failed to read monitor data from %s", path); ++ ++ (void)fclose(f); ++ ++out_freepath: ++ free(path); ++out_err: ++ return result; ++} ++ ++static unsigned int ++nsm_load_dir(const char *directory, nsm_populate_t func) ++{ ++ unsigned int count = 0; ++ struct dirent *de; ++ char *path; ++ DIR *dir; ++ ++ path = nsm_make_pathname(directory); ++ if (path == NULL) { ++ xlog(L_ERROR, "Failed to allocate path for directory %s", ++ directory); ++ return 0; ++ } ++ ++ dir = opendir(path); ++ free(path); ++ if (dir == NULL) { ++ xlog(L_ERROR, "Failed to open directory %s: %m", ++ directory); ++ return 0; ++ } ++ ++ while ((de = readdir(dir)) != NULL) { ++ if (de->d_type != (unsigned char)DT_REG) ++ continue; ++ if (de->d_name[0] == '.') ++ continue; ++ ++ count += nsm_load_host(directory, de->d_name, func); ++ } ++ ++ (void)closedir(dir); ++ return count; ++} ++ ++/** ++ * nsm_load_monitor_list - load list of hosts to monitor ++ * @func: callback function to create entry for one host ++ * ++ * Returns the count of hosts that were found in the directory. ++ */ ++unsigned int ++nsm_load_monitor_list(nsm_populate_t func) ++{ ++ return nsm_load_dir(NSM_MONITOR_DIR, func); ++} ++ ++/** ++ * nsm_load_notify_list - load list of hosts to notify ++ * @func: callback function to create entry for one host ++ * ++ * Returns the count of hosts that were found in the directory. ++ */ ++unsigned int ++nsm_load_notify_list(nsm_populate_t func) ++{ ++ return nsm_load_dir(NSM_NOTIFY_DIR, func); ++} ++ ++static void ++nsm_delete_host(const char *directory, const char *hostname) ++{ ++ char *path; ++ ++ path = nsm_make_record_pathname(directory, hostname); ++ if (path == NULL) { ++ xlog(L_ERROR, "Bad filename, not deleting"); ++ return; ++ } ++ ++ if (unlink(path) == -1) ++ xlog(L_ERROR, "Failed to unlink %s: %m", path); ++ ++ free(path); ++} ++ ++/** ++ * nsm_delete_monitored_host - delete on-disk record for monitored host ++ * @hostname: '\0'-terminated C string containing hostname of record to delete ++ * ++ */ ++void ++nsm_delete_monitored_host(const char *hostname) ++{ ++ nsm_delete_host(NSM_MONITOR_DIR, hostname); ++} ++ ++/** ++ * nsm_delete_notified_host - delete on-disk host record after notification ++ * @hostname: '\0'-terminated C string containing hostname of record to delete ++ * ++ */ ++void ++nsm_delete_notified_host(const char *hostname) ++{ ++ nsm_delete_host(NSM_NOTIFY_DIR, hostname); ++} +diff -up nfs-utils-1.2.1/support/nsm/Makefile.am.orig nfs-utils-1.2.1/support/nsm/Makefile.am +--- nfs-utils-1.2.1/support/nsm/Makefile.am.orig 2010-01-12 06:07:40.759815628 -0500 ++++ nfs-utils-1.2.1/support/nsm/Makefile.am 2010-01-12 06:07:40.759815628 -0500 +@@ -0,0 +1,45 @@ ++## Process this file with automake to produce Makefile.in ++ ++GENFILES_CLNT = sm_inter_clnt.c ++GENFILES_SVC = sm_inter_svc.c ++GENFILES_XDR = sm_inter_xdr.c ++GENFILES_H = sm_inter.h ++ ++GENFILES = $(GENFILES_CLNT) $(GENFILES_SVC) $(GENFILES_XDR) $(GENFILES_H) ++ ++EXTRA_DIST = sm_inter.x ++ ++noinst_LIBRARIES = libnsm.a ++libnsm_a_SOURCES = $(GENFILES) file.c ++ ++BUILT_SOURCES = $(GENFILES) ++ ++if CONFIG_RPCGEN ++RPCGEN = $(top_builddir)/tools/rpcgen/rpcgen ++$(RPCGEN): ++ make -C ../../tools/rpcgen all ++else ++RPCGEN = @RPCGEN_PATH@ ++endif ++ ++$(GENFILES_CLNT): %_clnt.c: %.x $(RPCGEN) ++ test -f $@ && rm -rf $@ || true ++ $(RPCGEN) -l -o $@ $< ++ ++$(GENFILES_SVC): %_svc.c: %.x $(RPCGEN) ++ test -f $@ && rm -rf $@ || true ++ $(RPCGEN) -m -o $@ $< ++ ++$(GENFILES_XDR): %_xdr.c: %.x $(RPCGEN) ++ test -f $@ && rm -rf $@ || true ++ $(RPCGEN) -c -o $@ $< ++ ++$(GENFILES_H): %.h: %.x $(RPCGEN) ++ test -f $@ && rm -rf $@ || true ++ $(RPCGEN) -h -o $@ $< ++ rm -f $(top_builddir)/support/include/sm_inter.h ++ $(LN_S) ../nsm/sm_inter.h $(top_builddir)/support/include/sm_inter.h ++ ++MAINTAINERCLEANFILES = Makefile.in ++ ++CLEANFILES = $(GENFILES) $(top_builddir)/support/include/sm_inter.h +diff -up nfs-utils-1.2.1/support/nsm/sm_inter.x.orig nfs-utils-1.2.1/support/nsm/sm_inter.x +--- nfs-utils-1.2.1/support/nsm/sm_inter.x.orig 2010-01-12 06:07:40.761826674 -0500 ++++ nfs-utils-1.2.1/support/nsm/sm_inter.x 2010-01-12 06:07:40.761826674 -0500 +@@ -0,0 +1,131 @@ ++/* ++ * Copyright (C) 1986 Sun Microsystems, Inc. ++ * Modified by Jeffrey A. Uphoff, 1995, 1997-1999. ++ * Modified by Olaf Kirch, 1996. ++ * Modified by H.J. Lu, 1998. ++ * ++ * NSM for Linux. ++ */ ++ ++/* ++ * Copyright (c) 2009, Sun Microsystems, Inc. ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * - Redistributions of source code must retain the above copyright notice, ++ * this list of conditions and the following disclaimer. ++ * - Redistributions in binary form must reproduce the above copyright notice, ++ * this list of conditions and the following disclaimer in the documentation ++ * and/or other materials provided with the distribution. ++ * - Neither the name of Sun Microsystems, Inc. nor the names of its ++ * contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++/* ++ * Status monitor protocol specification ++ */ ++ ++#ifdef RPC_CLNT ++%#include ++#endif ++ ++program SM_PROG { ++ version SM_VERS { ++ /* res_stat = stat_succ if status monitor agrees to monitor */ ++ /* res_stat = stat_fail if status monitor cannot monitor */ ++ /* if res_stat == stat_succ, state = state number of site sm_name */ ++ struct sm_stat_res SM_STAT(struct sm_name) = 1; ++ ++ /* res_stat = stat_succ if status monitor agrees to monitor */ ++ /* res_stat = stat_fail if status monitor cannot monitor */ ++ /* stat consists of state number of local site */ ++ struct sm_stat_res SM_MON(struct mon) = 2; ++ ++ /* stat consists of state number of local site */ ++ struct sm_stat SM_UNMON(struct mon_id) = 3; ++ ++ /* stat consists of state number of local site */ ++ struct sm_stat SM_UNMON_ALL(struct my_id) = 4; ++ ++ void SM_SIMU_CRASH(void) = 5; ++ ++ void SM_NOTIFY(struct stat_chge) = 6; ++ ++ } = 1; ++} = 100024; ++ ++const SM_MAXSTRLEN = 1024; ++const SM_PRIV_SIZE = 16; ++ ++struct sm_name { ++ string mon_name; ++}; ++ ++struct my_id { ++ string my_name; /* name of the site iniates the monitoring request*/ ++ int my_prog; /* rpc program # of the requesting process */ ++ int my_vers; /* rpc version # of the requesting process */ ++ int my_proc; /* rpc procedure # of the requesting process */ ++}; ++ ++struct mon_id { ++ string mon_name; /* name of the site to be monitored */ ++ struct my_id my_id; ++}; ++ ++ ++struct mon { ++ struct mon_id mon_id; ++ opaque priv[SM_PRIV_SIZE]; /* private information to store at monitor for requesting process */ ++}; ++ ++struct stat_chge { ++ string mon_name; /* name of the site that had the state change */ ++ int state; ++}; ++ ++/* ++ * state # of status monitor monitonically increases each time ++ * status of the site changes: ++ * an even number (>= 0) indicates the site is down and ++ * an odd number (> 0) indicates the site is up; ++ */ ++struct sm_stat { ++ int state; /* state # of status monitor */ ++}; ++ ++enum res { ++ stat_succ = 0, /* status monitor agrees to monitor */ ++ stat_fail = 1 /* status monitor cannot monitor */ ++}; ++ ++struct sm_stat_res { ++ res res_stat; ++ int state; ++}; ++ ++/* ++ * structure of the status message sent back by the status monitor ++ * when monitor site status changes ++ */ ++struct status { ++ string mon_name; ++ int state; ++ opaque priv[SM_PRIV_SIZE]; /* stored private information */ ++}; ++ ++%#define SM_INTER_X +diff -up nfs-utils-1.2.1/tests/Makefile.am.orig nfs-utils-1.2.1/tests/Makefile.am +--- nfs-utils-1.2.1/tests/Makefile.am.orig 2010-01-12 06:07:40.761826674 -0500 ++++ nfs-utils-1.2.1/tests/Makefile.am 2010-01-12 06:07:40.762888773 -0500 +@@ -0,0 +1,13 @@ ++## Process this file with automake to produce Makefile.in ++ ++check_PROGRAMS = statdb_dump ++statdb_dump_SOURCES = statdb_dump.c ++ ++statdb_dump_LDADD = ../support/nfs/libnfs.a \ ++ ../support/nsm/libnsm.a $(LIBCAP) ++ ++SUBDIRS = nsm_client ++ ++MAINTAINERCLEANFILES = Makefile.in ++ ++TESTS = t0001-statd-basic-mon-unmon.sh +diff -up nfs-utils-1.2.1/tests/nsm_client/Makefile.am.orig nfs-utils-1.2.1/tests/nsm_client/Makefile.am +--- nfs-utils-1.2.1/tests/nsm_client/Makefile.am.orig 2010-01-12 06:07:40.762888773 -0500 ++++ nfs-utils-1.2.1/tests/nsm_client/Makefile.am 2010-01-12 06:07:40.762888773 -0500 +@@ -0,0 +1,45 @@ ++## Process this file with automake to produce Makefile.in ++ ++GENFILES_CLNT = nlm_sm_inter_clnt.c ++GENFILES_SVC = nlm_sm_inter_svc.c ++GENFILES_XDR = nlm_sm_inter_xdr.c ++GENFILES_H = nlm_sm_inter.h ++ ++GENFILES = $(GENFILES_CLNT) $(GENFILES_SVC) $(GENFILES_XDR) $(GENFILES_H) ++ ++ ++check_PROGRAMS = nsm_client ++nsm_client_SOURCES = $(GENFILES) nsm_client.c ++ ++BUILT_SOURCES = $(GENFILES) ++nsm_client_LDADD = ../../support/nfs/libnfs.a \ ++ ../../support/nsm/libnsm.a $(LIBCAP) ++ ++if CONFIG_RPCGEN ++RPCGEN = $(top_builddir)/tools/rpcgen/rpcgen ++$(RPCGEN): ++ make -C ../../tools/rpcgen all ++else ++RPCGEN = @RPCGEN_PATH@ ++endif ++ ++$(GENFILES_CLNT): %_clnt.c: %.x $(RPCGEN) ++ test -f $@ && rm -rf $@ || true ++ $(RPCGEN) -l -o $@ $< ++ ++$(GENFILES_SVC): %_svc.c: %.x $(RPCGEN) ++ test -f $@ && rm -rf $@ || true ++ $(RPCGEN) -m -o $@ $< ++ ++$(GENFILES_XDR): %_xdr.c: %.x $(RPCGEN) ++ test -f $@ && rm -rf $@ || true ++ $(RPCGEN) -c -o $@ $< ++ ++$(GENFILES_H): %.h: %.x $(RPCGEN) ++ test -f $@ && rm -rf $@ || true ++ $(RPCGEN) -h -o $@ $< ++ ++MAINTAINERCLEANFILES = Makefile.in ++ ++CLEANFILES = $(GENFILES) ++ +diff -up nfs-utils-1.2.1/tests/nsm_client/nlm_sm_inter.x.orig nfs-utils-1.2.1/tests/nsm_client/nlm_sm_inter.x +--- nfs-utils-1.2.1/tests/nsm_client/nlm_sm_inter.x.orig 2010-01-12 06:07:40.763888991 -0500 ++++ nfs-utils-1.2.1/tests/nsm_client/nlm_sm_inter.x 2010-01-12 06:07:40.763888991 -0500 +@@ -0,0 +1,43 @@ ++/* ++ * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff ++ * Modified by Olaf Kirch, 1996. ++ * Modified by H.J. Lu, 1998. ++ * Modified by Jeff Layton, 2010. ++ * ++ * NLM similator for Linux ++ */ ++ ++#ifdef RPC_CLNT ++%#include ++#endif ++ ++/* ++ * statd rejects monitor registrations for any non-lockd services, so pretend ++ * to be lockd when testing. Furthermore, the only call we care about from ++ * statd is #16, which is the downcall to notify the kernel of a host's status ++ * change. ++ */ ++program NLM_SM_PROG { ++ /* version 3 of the NLM protocol */ ++ version NLM_SM_VERS3 { ++ void NLM_SM_NOTIFY(struct nlm_sm_notify) = 16; ++ } = 3; ++ ++ /* version 2 of NLM protocol */ ++ version NLM_SM_VERS4 { ++ void NLM_SM_NOTIFY(struct nlm_sm_notify) = 16; ++ } = 4; ++} = 100021; ++ ++const SM_MAXSTRLEN = 1024; ++const SM_PRIV_SIZE = 16; ++ ++/* ++ * structure of the status message sent back by the status monitor ++ * when monitor site status changes ++ */ ++struct nlm_sm_notify { ++ string mon_name; ++ int state; ++ opaque priv[SM_PRIV_SIZE]; /* stored private information */ ++}; +diff -up nfs-utils-1.2.1/tests/nsm_client/nsm_client.c.orig nfs-utils-1.2.1/tests/nsm_client/nsm_client.c +--- nfs-utils-1.2.1/tests/nsm_client/nsm_client.c.orig 2010-01-12 06:07:40.764889070 -0500 ++++ nfs-utils-1.2.1/tests/nsm_client/nsm_client.c 2010-01-12 06:07:40.764889070 -0500 +@@ -0,0 +1,465 @@ ++/* ++ * nsm_client.c -- synthetic client and lockd simulator for testing statd ++ * ++ * Copyright (C) 2010 Red Hat, Jeff Layton ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ * ++ * Very loosely based on "simulator.c" in the statd directory. Original ++ * copyright for that program follows: ++ * ++ * Copyright (C) 1995-1997, 1999 Jeffrey A. Uphoff ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "nfslib.h" ++#include "nfsrpc.h" ++#include "nsm.h" ++#include "sm_inter.h" ++#include "nlm_sm_inter.h" ++#include "sockaddr.h" ++#include "xcommon.h" ++ ++static void daemon_simulator(void); ++static void sim_killer(int sig); ++static int nsm_client_crash(char *); ++static int nsm_client_mon(char *, char *, char *, char *, int, int); ++static int nsm_client_stat(char *, char *); ++static int nsm_client_notify(char *, char *, char *); ++static int nsm_client_unmon(char *, char *, char *, int, int); ++static int nsm_client_unmon_all(char *, char *, int, int); ++ ++extern void nlm_sm_prog_4(struct svc_req *rqstp, register SVCXPRT *transp); ++extern void svc_exit(void); ++ ++/* ++ * default to 15 retransmit interval, which seems to be the default for ++ * UDP clients w/ legacy glibc RPC ++ */ ++static struct timeval retrans_interval = ++{ ++ .tv_sec = 15, ++}; ++ ++static struct option longopts[] = ++{ ++ { "help", 0, 0, 'h' }, ++ { "host", 0, 0, 'H' }, ++ { "name", 1, 0, 'n' }, ++ { "program", 1, 0, 'P' }, ++ { "version", 1, 0, 'v' }, ++ { NULL, 0, 0, 0 }, ++}; ++ ++static int ++usage(char *program) ++{ ++ printf("Usage:\n"); ++ printf("%s [options] [arg]...\n", program); ++ printf("where command is one of these with the specified args:\n"); ++ printf("crash\t\t\t\ttell host to simulate crash\n"); ++ printf("daemon\t\t\t\t\tstart up lockd daemon simulator\n"); ++ printf("notify \tsend a reboot notification to host\n"); ++ printf("stat \t\t\tget status of on host\n"); ++ printf("unmon_all\t\t\ttell host to unmon everything\n"); ++ printf("unmon \t\t\ttell host to unmon \n"); ++ printf("mon \t\ttell host to monitor with private \n"); ++ return 1; ++} ++ ++static int ++hex2bin(char *dst, size_t dstlen, char *src) ++{ ++ int i; ++ unsigned int tmp; ++ ++ for (i = 0; *src && i < dstlen; i++) { ++ if (sscanf(src, "%2x", &tmp) != 1) ++ return 0; ++ dst[i] = tmp; ++ src++; ++ if (!*src) ++ break; ++ src++; ++ } ++ ++ return 1; ++} ++ ++static void ++bin2hex(char *dst, char *src, size_t srclen) ++{ ++ int i; ++ ++ for (i = 0; i < srclen; i++) ++ dst += sprintf(dst, "%02x", 0xff & src[i]); ++} ++ ++int ++main(int argc, char **argv) ++{ ++ int arg, err = 0; ++ int remaining_args; ++ char my_name[NI_MAXHOST], host[NI_MAXHOST]; ++ char cookie[SM_PRIV_SIZE]; ++ int my_prog = NLM_SM_PROG; ++ int my_vers = NLM_SM_VERS4; ++ ++ my_name[0] = '\0'; ++ host[0] = '\0'; ++ ++ while ((arg = getopt_long(argc, argv, "hHn:P:v:", longopts, ++ NULL)) != EOF) { ++ switch (arg) { ++ case 'H': ++ strncpy(host, optarg, sizeof(host)); ++ case 'n': ++ strncpy(my_name, optarg, sizeof(my_name)); ++ case 'P': ++ my_prog = atoi(optarg); ++ case 'v': ++ my_vers = atoi(optarg); ++ } ++ } ++ ++ remaining_args = argc - optind; ++ if (remaining_args <= 0) ++ usage(argv[0]); ++ ++ if (!my_name[0]) ++ gethostname(my_name, sizeof(my_name)); ++ if (!host[0]) ++ strncpy(host, "127.0.0.1", sizeof(host)); ++ ++ if (!strcasecmp(argv[optind], "daemon")) { ++ daemon_simulator(); ++ } else if (!strcasecmp(argv[optind], "crash")) { ++ err = nsm_client_crash(host); ++ } else if (!strcasecmp(argv[optind], "stat")) { ++ if (remaining_args < 2) ++ usage(argv[0]); ++ err = nsm_client_stat(host, argv[optind + 2]); ++ } else if (!strcasecmp(argv[optind], "unmon_all")) { ++ err = nsm_client_unmon_all(host, my_name, my_prog, my_vers); ++ } else if (!strcasecmp(argv[optind], "unmon")) { ++ if (remaining_args < 2) ++ usage(argv[0]); ++ err = nsm_client_unmon(host, argv[optind + 1], my_name, my_prog, ++ my_vers); ++ } else if (!strcasecmp(argv[optind], "notify")) { ++ if (remaining_args < 2) ++ usage(argv[0]); ++ err = nsm_client_notify(host, argv[optind + 1], ++ argv[optind + 2]); ++ } else if (!strcasecmp(argv[optind], "mon")) { ++ if (remaining_args < 2) ++ usage(argv[0]); ++ ++ memset(cookie, '\0', SM_PRIV_SIZE); ++ if (!hex2bin(cookie, sizeof(cookie), argv[optind + 2])) { ++ fprintf(stderr, "SYS:%d\n", EINVAL); ++ printf("Unable to convert hex cookie %s to binary.\n", ++ argv[optind + 2]); ++ return 1; ++ } ++ ++ err = nsm_client_mon(host, argv[optind + 1], cookie, my_name, ++ my_prog, my_vers); ++ } else { ++ err = usage(argv[0]); ++ } ++ ++ return err; ++} ++ ++static CLIENT * ++nsm_client_get_rpcclient(const char *node) ++{ ++ unsigned short port; ++ struct addrinfo *ai; ++ struct addrinfo hints = { .ai_flags = AI_ADDRCONFIG }; ++ int err; ++ CLIENT *client = NULL; ++ ++#ifndef IPV6_ENABLED ++ hints.ai_family = AF_INET; ++#endif /* IPV6_ENABLED */ ++ ++ /* FIXME: allow support for providing port? */ ++ err = getaddrinfo(node, NULL, &hints, &ai); ++ if (err) { ++ fprintf(stderr, "EAI:%d\n", err); ++ if (err == EAI_SYSTEM) ++ fprintf(stderr, "SYS:%d\n", errno); ++ printf("Unable to translate host to address: %s\n", ++ err == EAI_SYSTEM ? strerror(errno) : ++ gai_strerror(err)); ++ return client; ++ } ++ ++ /* FIXME: allow for TCP too? */ ++ port = nfs_getport(ai->ai_addr, ai->ai_addrlen, SM_PROG, ++ SM_VERS, IPPROTO_UDP); ++ if (!port) { ++ fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); ++ printf("Unable to determine port for service\n"); ++ goto out; ++ } ++ ++ nfs_set_port(ai->ai_addr, port); ++ ++ client = nfs_get_rpcclient(ai->ai_addr, ai->ai_addrlen, IPPROTO_UDP, ++ SM_PROG, SM_VERS, &retrans_interval); ++ if (!client) { ++ fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); ++ printf("RPC client creation failed\n"); ++ } ++out: ++ freeaddrinfo(ai); ++ return client; ++} ++ ++static int ++nsm_client_mon(char *calling, char *monitoring, char *cookie, char *my_name, ++ int my_prog, int my_vers) ++{ ++ CLIENT *client; ++ sm_stat_res *result; ++ mon mon; ++ int err = 0; ++ ++ printf("Calling %s (as %s) to monitor %s\n", calling, my_name, ++ monitoring); ++ ++ if ((client = nsm_client_get_rpcclient(calling)) == NULL) ++ return 1; ++ ++ memcpy(mon.priv, cookie, SM_PRIV_SIZE); ++ mon.mon_id.my_id.my_name = my_name; ++ mon.mon_id.my_id.my_prog = my_prog; ++ mon.mon_id.my_id.my_vers = my_vers; ++ mon.mon_id.my_id.my_proc = NLM_SM_NOTIFY; ++ mon.mon_id.mon_name = monitoring; ++ ++ if (!(result = sm_mon_1(&mon, client))) { ++ fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); ++ printf("%s\n", clnt_sperror(client, "sm_mon_1")); ++ err = 1; ++ goto mon_out; ++ } ++ ++ printf("SM_MON request %s, state: %d\n", ++ result->res_stat == stat_succ ? "successful" : "failed", ++ result->state); ++ ++ if (result->res_stat != stat_succ) { ++ fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); ++ err = 1; ++ } ++ ++mon_out: ++ clnt_destroy(client); ++ return err; ++} ++ ++static int ++nsm_client_unmon(char *calling, char *unmonitoring, char *my_name, int my_prog, ++ int my_vers) ++{ ++ CLIENT *client; ++ sm_stat *result; ++ mon_id mon_id; ++ int err = 0; ++ ++ printf("Calling %s (as %s) to unmonitor %s\n", calling, my_name, ++ unmonitoring); ++ ++ if ((client = nsm_client_get_rpcclient(calling)) == NULL) ++ return 1; ++ ++ mon_id.my_id.my_name = my_name; ++ mon_id.my_id.my_prog = my_prog; ++ mon_id.my_id.my_vers = my_vers; ++ mon_id.my_id.my_proc = NLM_SM_NOTIFY; ++ mon_id.mon_name = unmonitoring; ++ ++ if (!(result = sm_unmon_1(&mon_id, client))) { ++ fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); ++ printf("%s\n", clnt_sperror(client, "sm_unmon_1")); ++ err = 1; ++ goto unmon_out; ++ } ++ ++ printf("SM_UNMON state: %d\n", result->state); ++ ++unmon_out: ++ clnt_destroy(client); ++ return err; ++} ++ ++static int ++nsm_client_unmon_all(char *calling, char *my_name, int my_prog, int my_vers) ++{ ++ CLIENT *client; ++ sm_stat *result; ++ my_id my_id; ++ int err = 0; ++ ++ printf("Calling %s (as %s) to unmonitor all hosts\n", calling, my_name); ++ ++ if ((client = nsm_client_get_rpcclient(calling)) == NULL) { ++ printf("RPC client creation failed\n"); ++ return 1; ++ } ++ ++ my_id.my_name = my_name; ++ my_id.my_prog = my_prog; ++ my_id.my_vers = my_vers; ++ my_id.my_proc = NLM_SM_NOTIFY; ++ ++ if (!(result = sm_unmon_all_1(&my_id, client))) { ++ fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); ++ printf("%s\n", clnt_sperror(client, "sm_unmon_all_1")); ++ err = 1; ++ goto unmon_all_out; ++ } ++ ++ printf("SM_UNMON_ALL state: %d\n", result->state); ++ ++unmon_all_out: ++ return err; ++} ++ ++static int ++nsm_client_crash(char *host) ++{ ++ CLIENT *client; ++ ++ if ((client = nsm_client_get_rpcclient(host)) == NULL) ++ return 1; ++ ++ if (!sm_simu_crash_1(NULL, client)) { ++ fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); ++ printf("%s\n", clnt_sperror(client, "sm_simu_crash_1")); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static int ++nsm_client_stat(char *calling, char *monitoring) ++{ ++ CLIENT *client; ++ sm_name checking; ++ sm_stat_res *result; ++ ++ if ((client = nsm_client_get_rpcclient(calling)) == NULL) ++ return 1; ++ ++ checking.mon_name = monitoring; ++ ++ if (!(result = sm_stat_1(&checking, client))) { ++ fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); ++ printf("%s\n", clnt_sperror(client, "sm_stat_1")); ++ return 1; ++ } ++ ++ if (result->res_stat != stat_succ) { ++ fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); ++ printf("stat_fail from %s for %s, state: %d\n", calling, ++ monitoring, result->state); ++ return 1; ++ } ++ ++ printf("stat_succ from %s for %s, state: %d\n", calling, ++ monitoring, result->state); ++ ++ return 0; ++} ++ ++static int ++nsm_client_notify(char *calling, char *mon_name, char *statestr) ++{ ++ CLIENT *client; ++ ++ stat_chge stat_chge = { .mon_name = mon_name }; ++ ++ stat_chge.state = atoi(statestr); ++ ++ if ((client = nsm_client_get_rpcclient(calling)) == NULL) ++ return 1; ++ ++ if (!sm_notify_1(&stat_chge, client)) { ++ fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); ++ printf("%s\n", clnt_sperror(client, "sm_notify_1")); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static void sim_killer(int sig) ++{ ++#ifdef HAVE_LIBTIRPC ++ (void) rpcb_unset(NLM_SM_PROG, NLM_SM_VERS4, NULL); ++#else ++ (void) pmap_unset(NLM_SM_PROG, NLM_SM_VERS4); ++#endif ++ exit(0); ++} ++ ++static void daemon_simulator(void) ++{ ++ signal(SIGHUP, sim_killer); ++ signal(SIGINT, sim_killer); ++ signal(SIGTERM, sim_killer); ++ /* FIXME: allow for different versions? */ ++ nfs_svc_create("nlmsim", NLM_SM_PROG, NLM_SM_VERS4, nlm_sm_prog_4, 0); ++ svc_run(); ++} ++ ++void *nlm_sm_notify_4_svc(struct nlm_sm_notify *argp, struct svc_req *rqstp) ++{ ++ static char *result; ++ char priv[SM_PRIV_SIZE * 2 + 1]; ++ ++ bin2hex(priv, argp->priv, SM_PRIV_SIZE); ++ ++ printf("state=%d:mon_name=%s:private=%s\n", argp->state, ++ argp->mon_name, priv); ++ return (void *) &result; ++} ++ ++void *nlm_sm_notify_3_svc(struct nlm_sm_notify *argp, struct svc_req *rqstp) ++{ ++ return nlm_sm_notify_4_svc(argp, rqstp); ++} +diff -up nfs-utils-1.2.1/tests/nsm_client/README.orig nfs-utils-1.2.1/tests/nsm_client/README +--- nfs-utils-1.2.1/tests/nsm_client/README.orig 2010-01-12 06:07:40.763888991 -0500 ++++ nfs-utils-1.2.1/tests/nsm_client/README 2010-01-12 06:07:40.763888991 -0500 +@@ -0,0 +1,12 @@ ++The nsm_client program is intended for testing statd. It has the ability ++to act as a synthetic NSM client for sending artificial NSM calls to any ++host you choose. ++ ++It also has an NLM simulator that implements the call that statd uses to ++communicate with lockd. The daemon simulator will start itself up, ++register as an NLM service and listen for "downcalls" from statd. When ++it gets one, it will log a message. ++ ++Note that lockd will need to be down when using the daemon simulator. It ++also does not implement the entire NLM protocol and is only really ++useful for testing statd's downcall. +diff -up nfs-utils-1.2.1/tests/statdb_dump.c.orig nfs-utils-1.2.1/tests/statdb_dump.c +--- nfs-utils-1.2.1/tests/statdb_dump.c.orig 2010-01-12 06:07:40.765878933 -0500 ++++ nfs-utils-1.2.1/tests/statdb_dump.c 2010-01-12 06:07:40.765878933 -0500 +@@ -0,0 +1,99 @@ ++/* ++ * statdb_dump.c -- dump contents of statd's monitor DB ++ * ++ * Copyright (C) 2010 Red Hat, Jeff Layton ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif ++ ++#include ++#include ++#include ++ ++#include "nsm.h" ++#include "xlog.h" ++ ++static char cookiebuf[(SM_PRIV_SIZE * 2) + 1]; ++static char addrbuf[INET6_ADDRSTRLEN + 1]; ++ ++static unsigned int ++dump_host(const char *hostname, const struct sockaddr *sa, const struct mon *m, ++ const time_t timestamp) ++{ ++ int ret; ++ const char *addr; ++ const struct sockaddr_in *sin; ++ const struct sockaddr_in6 *sin6; ++ ++ ret = nsm_priv_to_hex(m->priv, cookiebuf, sizeof(cookiebuf)); ++ if (!ret) { ++ xlog(L_ERROR, "Unable to convert cookie to hex string.\n"); ++ return ret; ++ } ++ ++ switch (sa->sa_family) { ++ case AF_INET: ++ sin = (struct sockaddr_in *)(char *)sa; ++ addr = inet_ntop(sa->sa_family, &sin->sin_addr.s_addr, addrbuf, ++ (socklen_t)sizeof(addrbuf)); ++ break; ++ case AF_INET6: ++ sin6 = (struct sockaddr_in6 *)(char *)sa; ++ addr = inet_ntop(sa->sa_family, &sin6->sin6_addr, addrbuf, ++ (socklen_t)sizeof(addrbuf)); ++ break; ++ default: ++ xlog(L_ERROR, "Unrecognized address family: %hu\n", ++ sa->sa_family); ++ return 0; ++ } ++ ++ if (addr == NULL) { ++ xlog(L_ERROR, "Unable to convert sockaddr to string: %s\n", ++ strerror(errno)); ++ return 0; ++ } ++ ++ /* ++ * Callers of this program should assume that in the future, extra ++ * fields may be added to the output. Anyone adding extra fields to ++ * the output should add them to the end of the line. ++ */ ++ printf("%s %s %s %s %s %d %d %d\n", ++ hostname, addr, cookiebuf, ++ m->mon_id.mon_name, ++ m->mon_id.my_id.my_name, ++ m->mon_id.my_id.my_prog, ++ m->mon_id.my_id.my_vers, ++ m->mon_id.my_id.my_proc); ++ ++ return 1; ++} ++ ++int ++main(int argc, char **argv) ++{ ++ xlog_syslog(0); ++ xlog_stderr(1); ++ xlog_open(argv[0]); ++ ++ nsm_load_monitor_list(dump_host); ++ return 0; ++} +diff -up nfs-utils-1.2.1/tests/t0001-statd-basic-mon-unmon.sh.orig nfs-utils-1.2.1/tests/t0001-statd-basic-mon-unmon.sh +--- nfs-utils-1.2.1/tests/t0001-statd-basic-mon-unmon.sh.orig 2010-01-12 06:07:40.765878933 -0500 ++++ nfs-utils-1.2.1/tests/t0001-statd-basic-mon-unmon.sh 2010-01-12 06:07:40.765878933 -0500 +@@ -0,0 +1,58 @@ ++#!/bin/bash ++# ++# statd_basic_mon_unmon -- test basic mon/unmon functionality with statd ++# ++# Copyright (C) 2010 Red Hat, Jeff Layton ++# ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation; either version 2 ++# of the License, or (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, Inc., ++# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++# ++ ++. ./test-lib.sh ++ ++# This test needs root privileges ++check_root ++ ++start_statd ++if [ $? -ne 0 ]; then ++ echo "FAIL: problem starting statd" ++ exit 1 ++fi ++ ++COOKIE=`echo $$ | md5sum | cut -d' ' -f1` ++MON_NAME=`hostname` ++ ++nsm_client mon $MON_NAME $COOKIE ++if [ $? -ne 0 ]; then ++ echo "FAIL: mon failed" ++ kill_statd ++ exit 1 ++fi ++ ++statdb_dump | grep $MON_NAME | grep -q $COOKIE ++if [ $? -ne 0 ]; then ++ echo "FAIL: monitor DB doesn't seem to contain entry" ++ kill_statd ++ exit 1 ++fi ++ ++nsm_client unmon $MON_NAME ++if [ $? -ne 0 ]; then ++ echo "FAIL: unmon failed" ++ kill_statd ++ exit 1 ++fi ++ ++kill_statd ++ +diff -up nfs-utils-1.2.1/tests/test-lib.sh.orig nfs-utils-1.2.1/tests/test-lib.sh +--- nfs-utils-1.2.1/tests/test-lib.sh.orig 2010-01-12 06:07:40.766878219 -0500 ++++ nfs-utils-1.2.1/tests/test-lib.sh 2010-01-12 06:07:40.766878219 -0500 +@@ -0,0 +1,60 @@ ++#!/bin/bash ++# ++# test-lib.sh -- library of functions for nfs-utils tests ++# ++# Copyright (C) 2010 Red Hat, Jeff Layton ++# ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation; either version 2 ++# of the License, or (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, Inc., ++# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++# ++ ++# make sure $srcdir is set and sanity check it ++srcdir=${srcdir-.} ++if [ ! -d ${srcdir} ]; then ++ echo "***ERROR***: bad installation -- \$srcdir=${srcdir}" ++ exit 1 ++fi ++ ++export PATH=$PATH:${srcdir}:${srcdir}/nsm_client ++ ++# Some tests require root privileges. Check for them and skip the test (exit 77) ++# if the caller doesn't have them. ++check_root() { ++ if [ $EUID -ne 0 ]; then ++ echo "*** Skipping this test as it requires root privs ***" ++ exit 77 ++ fi ++} ++ ++# is lockd registered as a service? ++lockd_registered() { ++ rpcinfo -p | grep -q nlockmgr ++ return $? ++} ++ ++# start up statd ++start_statd() { ++ rpcinfo -u 127.0.0.1 status 1 &> /dev/null ++ if [ $? -eq 0 ]; then ++ echo "***ERROR***: statd is already running and should " ++ echo " be down when starting this test" ++ return 1 ++ fi ++ $srcdir/../utils/statd/statd --no-notify ++} ++ ++# shut down statd ++kill_statd() { ++ kill `cat /var/run/rpc.statd.pid` ++} +diff -up nfs-utils-1.2.1/utils/gssd/gssd.c.orig nfs-utils-1.2.1/utils/gssd/gssd.c +--- nfs-utils-1.2.1/utils/gssd/gssd.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/gssd/gssd.c 2010-01-12 06:07:40.766878219 -0500 +@@ -56,7 +56,6 @@ + #include "krb5_util.h" + + char pipefs_dir[PATH_MAX] = GSSD_PIPEFS_DIR; +-char pipefs_nfsdir[PATH_MAX] = GSSD_PIPEFS_DIR; + char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE; + char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR; + char *ccachesearch[GSSD_MAX_CCACHE_SEARCH + 1]; +@@ -159,11 +158,6 @@ main(int argc, char *argv[]) + if (preferred_realm == NULL) + gssd_k5_get_default_realm(&preferred_realm); + +- snprintf(pipefs_nfsdir, sizeof(pipefs_nfsdir), "%s/%s", +- pipefs_dir, GSSD_SERVICE_NAME); +- if (pipefs_nfsdir[sizeof(pipefs_nfsdir)-1] != '\0') +- errx(1, "pipefs_nfsdir path name too long"); +- + if ((progname = strrchr(argv[0], '/'))) + progname++; + else +diff -up nfs-utils-1.2.1/utils/gssd/gssd.h.orig nfs-utils-1.2.1/utils/gssd/gssd.h +--- nfs-utils-1.2.1/utils/gssd/gssd.h.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/gssd/gssd.h 2010-01-12 06:07:40.767886262 -0500 +@@ -60,7 +60,6 @@ enum {AUTHTYPE_KRB5, AUTHTYPE_SPKM3, AUT + + + extern char pipefs_dir[PATH_MAX]; +-extern char pipefs_nfsdir[PATH_MAX]; + extern char keytabfile[PATH_MAX]; + extern char *ccachesearch[]; + extern int use_memcache; +@@ -83,13 +82,24 @@ struct clnt_info { + int krb5_poll_index; + int spkm3_fd; + int spkm3_poll_index; ++ int gssd_fd; ++ int gssd_poll_index; + struct sockaddr_storage addr; + }; + ++TAILQ_HEAD(topdirs_list_head, topdirs_info) topdirs_list; ++ ++struct topdirs_info { ++ TAILQ_ENTRY(topdirs_info) list; ++ char *dirname; ++ int fd; ++}; ++ + void init_client_list(void); + int update_client_list(void); + void handle_krb5_upcall(struct clnt_info *clp); + void handle_spkm3_upcall(struct clnt_info *clp); ++void handle_gssd_upcall(struct clnt_info *clp); + int gssd_acquire_cred(char *server_name); + void gssd_run(void); + +diff -up nfs-utils-1.2.1/utils/gssd/gssd_main_loop.c.orig nfs-utils-1.2.1/utils/gssd/gssd_main_loop.c +--- nfs-utils-1.2.1/utils/gssd/gssd_main_loop.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/gssd/gssd_main_loop.c 2010-01-12 06:07:40.768888711 -0500 +@@ -49,6 +49,7 @@ + #include + #include + #include ++#include + + #include "gssd.h" + #include "err_util.h" +@@ -73,6 +74,17 @@ scan_poll_results(int ret) + + for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) + { ++ i = clp->gssd_poll_index; ++ if (i >= 0 && pollarray[i].revents) { ++ if (pollarray[i].revents & POLLHUP) ++ dir_changed = 1; ++ if (pollarray[i].revents & POLLIN) ++ handle_gssd_upcall(clp); ++ pollarray[clp->gssd_poll_index].revents = 0; ++ ret--; ++ if (!ret) ++ break; ++ } + i = clp->krb5_poll_index; + if (i >= 0 && pollarray[i].revents) { + if (pollarray[i].revents & POLLHUP) +@@ -98,12 +110,85 @@ scan_poll_results(int ret) + } + }; + ++static int ++topdirs_add_entry(struct dirent *dent) ++{ ++ struct topdirs_info *tdi; ++ ++ tdi = calloc(sizeof(struct topdirs_info), 1); ++ if (tdi == NULL) { ++ printerr(0, "ERROR: Couldn't allocate struct topdirs_info\n"); ++ return -1; ++ } ++ tdi->dirname = malloc(PATH_MAX); ++ if (tdi->dirname == NULL) { ++ printerr(0, "ERROR: Couldn't allocate directory name\n"); ++ free(tdi); ++ return -1; ++ } ++ snprintf(tdi->dirname, PATH_MAX, "%s/%s", pipefs_dir, dent->d_name); ++ tdi->fd = open(tdi->dirname, O_RDONLY); ++ if (tdi->fd != -1) { ++ fcntl(tdi->fd, F_SETSIG, DNOTIFY_SIGNAL); ++ fcntl(tdi->fd, F_NOTIFY, ++ DN_CREATE|DN_DELETE|DN_MODIFY|DN_MULTISHOT); ++ } ++ ++ TAILQ_INSERT_HEAD(&topdirs_list, tdi, list); ++ return 0; ++} ++ ++static void ++topdirs_free_list(void) ++{ ++ struct topdirs_info *tdi; ++ ++ TAILQ_FOREACH(tdi, &topdirs_list, list) { ++ free(tdi->dirname); ++ if (tdi->fd != -1) ++ close(tdi->fd); ++ TAILQ_REMOVE(&topdirs_list, tdi, list); ++ free(tdi); ++ } ++} ++ ++static int ++topdirs_init_list(void) ++{ ++ DIR *pipedir; ++ struct dirent *dent; ++ int ret; ++ ++ TAILQ_INIT(&topdirs_list); ++ ++ pipedir = opendir(pipefs_dir); ++ if (pipedir == NULL) { ++ printerr(0, "ERROR: could not open rpc_pipefs directory '%s': " ++ "%s\n", pipefs_dir, strerror(errno)); ++ return -1; ++ } ++ for (dent = readdir(pipedir); dent != NULL; dent = readdir(pipedir)) { ++ if (dent->d_type != DT_DIR || ++ strcmp(dent->d_name, ".") == 0 || ++ strcmp(dent->d_name, "..") == 0) { ++ continue; ++ } ++ ret = topdirs_add_entry(dent); ++ if (ret) ++ goto out_err; ++ } ++ closedir(pipedir); ++ return 0; ++out_err: ++ topdirs_free_list(); ++ return -1; ++} ++ + void + gssd_run() + { + int ret; + struct sigaction dn_act; +- int fd; + sigset_t set; + + /* Taken from linux/Documentation/dnotify.txt: */ +@@ -117,13 +202,8 @@ gssd_run() + sigaddset(&set, DNOTIFY_SIGNAL); + sigprocmask(SIG_UNBLOCK, &set, NULL); + +- if ((fd = open(pipefs_nfsdir, O_RDONLY)) == -1) { +- printerr(0, "ERROR: failed to open %s: %s\n", +- pipefs_nfsdir, strerror(errno)); +- exit(1); +- } +- fcntl(fd, F_SETSIG, DNOTIFY_SIGNAL); +- fcntl(fd, F_NOTIFY, DN_CREATE|DN_DELETE|DN_MODIFY|DN_MULTISHOT); ++ if (topdirs_init_list() != 0) ++ return; + + init_client_list(); + +@@ -132,8 +212,7 @@ gssd_run() + while (dir_changed) { + dir_changed = 0; + if (update_client_list()) { +- printerr(0, "ERROR: couldn't update " +- "client list\n"); ++ /* Error msg is already printed */ + exit(1); + } + } +@@ -151,6 +230,7 @@ gssd_run() + scan_poll_results(ret); + } + } +- close(fd); ++ topdirs_free_list(); ++ + return; + } +diff -up nfs-utils-1.2.1/utils/gssd/gssd_proc.c.orig nfs-utils-1.2.1/utils/gssd/gssd_proc.c +--- nfs-utils-1.2.1/utils/gssd/gssd_proc.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/gssd/gssd_proc.c 2010-01-12 06:07:40.769878502 -0500 +@@ -73,6 +73,7 @@ + #include "krb5_util.h" + #include "context.h" + #include "nfsrpc.h" ++#include "nfslib.h" + + /* + * pollarray: +@@ -83,20 +84,22 @@ + * linked list of struct clnt_info which associates a clntXXX directory + * with an index into pollarray[], and other basic data about that client. + * +- * Directory structure: created by the kernel nfs client +- * {pipefs_nfsdir}/clntXX : one per rpc_clnt struct in the kernel +- * {pipefs_nfsdir}/clntXX/krb5 : read uid for which kernel wants ++ * Directory structure: created by the kernel ++ * {rpc_pipefs}/{dir}/clntXX : one per rpc_clnt struct in the kernel ++ * {rpc_pipefs}/{dir}/clntXX/krb5 : read uid for which kernel wants + * a context, write the resulting context +- * {pipefs_nfsdir}/clntXX/info : stores info such as server name ++ * {rpc_pipefs}/{dir}/clntXX/info : stores info such as server name ++ * {rpc_pipefs}/{dir}/clntXX/gssd : pipe for all gss mechanisms using ++ * a text-based string of parameters + * + * Algorithm: +- * Poll all {pipefs_nfsdir}/clntXX/krb5 files. When ready, data read +- * is a uid; performs rpcsec_gss context initialization protocol to ++ * Poll all {rpc_pipefs}/{dir}/clntXX/YYYY files. When data is ready, ++ * read and process; performs rpcsec_gss context initialization protocol to + * get a cred for that user. Writes result to corresponding krb5 file + * in a form the kernel code will understand. + * In addition, we make sure we are notified whenever anything is +- * created or destroyed in {pipefs_nfsdir} or in an of the clntXX directories, +- * and rescan the whole {pipefs_nfsdir} when this happens. ++ * created or destroyed in {rpc_pipefs} or in any of the clntXX directories, ++ * and rescan the whole {rpc_pipefs} when this happens. + */ + + struct pollfd * pollarray; +@@ -105,7 +108,7 @@ int pollsize; /* the size of pollaray ( + + /* + * convert a presentation address string to a sockaddr_storage struct. Returns +- * true on success and false on failure. ++ * true on success or false on failure. + * + * Note that we do not populate the sin6_scope_id field here for IPv6 addrs. + * gssd nececessarily relies on hostname resolution and DNS AAAA records +@@ -117,26 +120,43 @@ int pollsize; /* the size of pollaray ( + * not really feasible at present. + */ + static int +-addrstr_to_sockaddr(struct sockaddr *sa, const char *addr, const int port) ++addrstr_to_sockaddr(struct sockaddr *sa, const char *node, const char *port) + { +- struct sockaddr_in *s4 = (struct sockaddr_in *) sa; +-#ifdef IPV6_SUPPORTED +- struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa; +-#endif /* IPV6_SUPPORTED */ ++ int rc; ++ struct addrinfo *res; ++ struct addrinfo hints = { .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV }; + +- if (inet_pton(AF_INET, addr, &s4->sin_addr)) { +- s4->sin_family = AF_INET; +- s4->sin_port = htons(port); +-#ifdef IPV6_SUPPORTED +- } else if (inet_pton(AF_INET6, addr, &s6->sin6_addr)) { +- s6->sin6_family = AF_INET6; +- s6->sin6_port = htons(port); ++#ifndef IPV6_SUPPORTED ++ hints.ai_family = AF_INET; + #endif /* IPV6_SUPPORTED */ +- } else { +- printerr(0, "ERROR: unable to convert %s to address\n", addr); ++ ++ rc = getaddrinfo(node, port, &hints, &res); ++ if (rc) { ++ printerr(0, "ERROR: unable to convert %s|%s to sockaddr: %s\n", ++ node, port, rc == EAI_SYSTEM ? strerror(errno) : ++ gai_strerror(rc)); + return 0; + } + ++#ifdef IPV6_SUPPORTED ++ /* ++ * getnameinfo ignores the scopeid. If the address turns out to have ++ * a non-zero scopeid, we can't use it -- the resolved host might be ++ * completely different from the one intended. ++ */ ++ if (res->ai_addr->sa_family == AF_INET6) { ++ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)res->ai_addr; ++ if (sin6->sin6_scope_id) { ++ printerr(0, "ERROR: address %s has non-zero " ++ "sin6_scope_id!\n", node); ++ freeaddrinfo(res); ++ return 0; ++ } ++ } ++#endif /* IPV6_SUPPORTED */ ++ ++ memcpy(sa, res->ai_addr, res->ai_addrlen); ++ freeaddrinfo(res); + return 1; + } + +@@ -194,11 +214,10 @@ read_service_info(char *info_file_name, + char program[16]; + char version[16]; + char protoname[16]; +- char cb_port[128]; ++ char port[128]; + char *p; + int fd = -1; + int numfields; +- int port = 0; + + *servicename = *servername = *protocol = NULL; + +@@ -227,20 +246,22 @@ read_service_info(char *info_file_name, + goto fail; + } + +- cb_port[0] = '\0'; ++ port[0] = '\0'; + if ((p = strstr(buf, "port")) != NULL) +- sscanf(p, "port: %127s\n", cb_port); ++ sscanf(p, "port: %127s\n", port); + + /* check service, program, and version */ +- if(memcmp(service, "nfs", 3)) return -1; ++ if (memcmp(service, "nfs", 3) != 0) ++ return -1; + *prog = atoi(program + 1); /* skip open paren */ + *vers = atoi(version); +- if((*prog != 100003) || ((*vers != 2) && (*vers != 3) && (*vers != 4))) +- goto fail; + +- if (cb_port[0] != '\0') { +- port = atoi(cb_port); +- if (port < 0 || port > 65535) ++ if (strlen(service) == 3 ) { ++ if ((*prog != 100003) || ((*vers != 2) && (*vers != 3) && ++ (*vers != 4))) ++ goto fail; ++ } else if (memcmp(service, "nfs4_cb", 7) == 0) { ++ if (*vers != 1) + goto fail; + } + +@@ -281,9 +302,13 @@ destroy_client(struct clnt_info *clp) + if (clp->spkm3_poll_index != -1) + memset(&pollarray[clp->spkm3_poll_index], 0, + sizeof(struct pollfd)); ++ if (clp->gssd_poll_index != -1) ++ memset(&pollarray[clp->gssd_poll_index], 0, ++ sizeof(struct pollfd)); + if (clp->dir_fd != -1) close(clp->dir_fd); + if (clp->krb5_fd != -1) close(clp->krb5_fd); + if (clp->spkm3_fd != -1) close(clp->spkm3_fd); ++ if (clp->gssd_fd != -1) close(clp->gssd_fd); + free(clp->dirname); + free(clp->servicename); + free(clp->servername); +@@ -303,8 +328,10 @@ insert_new_clnt(void) + } + clp->krb5_poll_index = -1; + clp->spkm3_poll_index = -1; ++ clp->gssd_poll_index = -1; + clp->krb5_fd = -1; + clp->spkm3_fd = -1; ++ clp->gssd_fd = -1; + clp->dir_fd = -1; + + TAILQ_INSERT_HEAD(&clnt_list, clp, list); +@@ -315,19 +342,43 @@ out: + static int + process_clnt_dir_files(struct clnt_info * clp) + { +- char kname[32]; +- char sname[32]; +- char info_file_name[32]; +- +- if (clp->krb5_fd == -1) { +- snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname); +- clp->krb5_fd = open(kname, O_RDWR); +- } +- if (clp->spkm3_fd == -1) { +- snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname); +- clp->spkm3_fd = open(sname, O_RDWR); ++ char name[PATH_MAX]; ++ char gname[PATH_MAX]; ++ char info_file_name[PATH_MAX]; ++ ++ if (clp->gssd_fd == -1) { ++ snprintf(gname, sizeof(gname), "%s/gssd", clp->dirname); ++ clp->gssd_fd = open(gname, O_RDWR); ++ } ++ if (clp->gssd_fd == -1) { ++ if (clp->krb5_fd == -1) { ++ snprintf(name, sizeof(name), "%s/krb5", clp->dirname); ++ clp->krb5_fd = open(name, O_RDWR); ++ } ++ if (clp->spkm3_fd == -1) { ++ snprintf(name, sizeof(name), "%s/spkm3", clp->dirname); ++ clp->spkm3_fd = open(name, O_RDWR); ++ } ++ ++ /* If we opened a gss-specific pipe, let's try opening ++ * the new upcall pipe again. If we succeed, close ++ * gss-specific pipe(s). ++ */ ++ if (clp->krb5_fd != -1 || clp->spkm3_fd != -1) { ++ clp->gssd_fd = open(gname, O_RDWR); ++ if (clp->gssd_fd != -1) { ++ if (clp->krb5_fd != -1) ++ close(clp->krb5_fd); ++ clp->krb5_fd = -1; ++ if (clp->spkm3_fd != -1) ++ close(clp->spkm3_fd); ++ clp->spkm3_fd = -1; ++ } ++ } + } +- if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1)) ++ ++ if ((clp->krb5_fd == -1) && (clp->spkm3_fd == -1) && ++ (clp->gssd_fd == -1)) + return -1; + snprintf(info_file_name, sizeof(info_file_name), "%s/info", + clp->dirname); +@@ -362,6 +413,15 @@ get_poll_index(int *ind) + static int + insert_clnt_poll(struct clnt_info *clp) + { ++ if ((clp->gssd_fd != -1) && (clp->gssd_poll_index == -1)) { ++ if (get_poll_index(&clp->gssd_poll_index)) { ++ printerr(0, "ERROR: Too many gssd clients\n"); ++ return -1; ++ } ++ pollarray[clp->gssd_poll_index].fd = clp->gssd_fd; ++ pollarray[clp->gssd_poll_index].events |= POLLIN; ++ } ++ + if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) { + if (get_poll_index(&clp->krb5_poll_index)) { + printerr(0, "ERROR: Too many krb5 clients\n"); +@@ -384,17 +444,18 @@ insert_clnt_poll(struct clnt_info *clp) + } + + static void +-process_clnt_dir(char *dir) ++process_clnt_dir(char *dir, char *pdir) + { + struct clnt_info * clp; + + if (!(clp = insert_new_clnt())) + goto fail_destroy_client; + +- if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) { ++ /* An extra for the '/', and an extra for the null */ ++ if (!(clp->dirname = calloc(strlen(dir) + strlen(pdir) + 2, 1))) { + goto fail_destroy_client; + } +- memcpy(clp->dirname, dir, strlen(dir)); ++ sprintf(clp->dirname, "%s/%s", pdir, dir); + if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) { + printerr(0, "ERROR: can't open %s: %s\n", + clp->dirname, strerror(errno)); +@@ -438,16 +499,24 @@ init_client_list(void) + * directories, since the DNOTIFY could have been in there. + */ + static void +-update_old_clients(struct dirent **namelist, int size) ++update_old_clients(struct dirent **namelist, int size, char *pdir) + { + struct clnt_info *clp; + void *saveprev; + int i, stillhere; ++ char fname[PATH_MAX]; + + for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) { ++ /* only compare entries in the global list that are from the ++ * same pipefs parent directory as "pdir" ++ */ ++ if (strncmp(clp->dirname, pdir, strlen(pdir)) != 0) continue; ++ + stillhere = 0; + for (i=0; i < size; i++) { +- if (!strcmp(clp->dirname, namelist[i]->d_name)) { ++ snprintf(fname, sizeof(fname), "%s/%s", ++ pdir, namelist[i]->d_name); ++ if (strcmp(clp->dirname, fname) == 0) { + stillhere = 1; + break; + } +@@ -468,48 +537,69 @@ update_old_clients(struct dirent **namel + + /* Search for a client by directory name, return 1 if found, 0 otherwise */ + static int +-find_client(char *dirname) ++find_client(char *dirname, char *pdir) + { + struct clnt_info *clp; ++ char fname[PATH_MAX]; + +- for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) +- if (!strcmp(clp->dirname, dirname)) ++ for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) { ++ snprintf(fname, sizeof(fname), "%s/%s", pdir, dirname); ++ if (strcmp(clp->dirname, fname) == 0) + return 1; ++ } + return 0; + } + +-/* Used to read (and re-read) list of clients, set up poll array. */ +-int +-update_client_list(void) ++static int ++process_pipedir(char *pipe_name) + { + struct dirent **namelist; + int i, j; + +- if (chdir(pipefs_nfsdir) < 0) { ++ if (chdir(pipe_name) < 0) { + printerr(0, "ERROR: can't chdir to %s: %s\n", +- pipefs_nfsdir, strerror(errno)); ++ pipe_name, strerror(errno)); + return -1; + } + +- j = scandir(pipefs_nfsdir, &namelist, NULL, alphasort); ++ j = scandir(pipe_name, &namelist, NULL, alphasort); + if (j < 0) { + printerr(0, "ERROR: can't scandir %s: %s\n", +- pipefs_nfsdir, strerror(errno)); ++ pipe_name, strerror(errno)); + return -1; + } +- update_old_clients(namelist, j); ++ ++ update_old_clients(namelist, j, pipe_name); + for (i=0; i < j; i++) { + if (i < FD_ALLOC_BLOCK + && !strncmp(namelist[i]->d_name, "clnt", 4) +- && !find_client(namelist[i]->d_name)) +- process_clnt_dir(namelist[i]->d_name); ++ && !find_client(namelist[i]->d_name, pipe_name)) ++ process_clnt_dir(namelist[i]->d_name, pipe_name); + free(namelist[i]); + } + + free(namelist); ++ + return 0; + } + ++/* Used to read (and re-read) list of clients, set up poll array. */ ++int ++update_client_list(void) ++{ ++ int retval = -1; ++ struct topdirs_info *tdi; ++ ++ TAILQ_FOREACH(tdi, &topdirs_list, list) { ++ retval = process_pipedir(tdi->dirname); ++ if (retval) ++ printerr(1, "WARNING: error processing %s\n", ++ tdi->dirname); ++ ++ } ++ return retval; ++} ++ + static int + do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd, + gss_buffer_desc *context_token) +@@ -798,15 +888,14 @@ int create_auth_rpc_client(struct clnt_i + goto out; + } + +- + /* + * this code uses the userland rpcsec gss library to create a krb5 + * context on behalf of the kernel + */ +-void +-handle_krb5_upcall(struct clnt_info *clp) ++static void ++process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname, ++ char *service) + { +- uid_t uid; + CLIENT *rpc_clnt = NULL; + AUTH *auth = NULL; + struct authgss_private_data pd; +@@ -816,19 +905,43 @@ handle_krb5_upcall(struct clnt_info *clp + char **dirname; + int create_resp = -1; + +- printerr(1, "handling krb5 upcall\n"); ++ printerr(1, "handling krb5 upcall (%s)\n", clp->dirname); + ++ if (tgtname) { ++ if (clp->servicename) { ++ free(clp->servicename); ++ clp->servicename = strdup(tgtname); ++ } ++ } + token.length = 0; + token.value = NULL; + memset(&pd, 0, sizeof(struct authgss_private_data)); + +- if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) { +- printerr(0, "WARNING: failed reading uid from krb5 " +- "upcall pipe: %s\n", strerror(errno)); +- goto out; +- } +- +- if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0)) { ++ /* ++ * If "service" is specified, then the kernel is indicating that ++ * we must use machine credentials for this request. (Regardless ++ * of the uid value or the setting of root_uses_machine_creds.) ++ * If the service value is "*", then any service name can be used. ++ * Otherwise, it specifies the service name that should be used. ++ * (For now, the values of service will only be "*" or "nfs".) ++ * ++ * Restricting gssd to use "nfs" service name is needed for when ++ * the NFS server is doing a callback to the NFS client. In this ++ * case, the NFS server has to authenticate itself as "nfs" -- ++ * even if there are other service keys such as "host" or "root" ++ * in the keytab. ++ * ++ * Another case when the kernel may specify the service attribute ++ * is when gssd is being asked to create the context for a ++ * SETCLIENT_ID operation. In this case, machine credentials ++ * must be used for the authentication. However, the service name ++ * used for this case is not important. ++ * ++ */ ++ printerr(2, "%s: service is '%s'\n", __func__, ++ service ? service : ""); ++ if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 && ++ service == NULL)) { + /* Tell krb5 gss which credentials cache to use */ + for (dirname = ccachesearch; *dirname != NULL; dirname++) { + if (gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname) == 0) +@@ -839,12 +952,13 @@ handle_krb5_upcall(struct clnt_info *clp + } + } + if (create_resp != 0) { +- if (uid == 0 && root_uses_machine_creds == 1) { ++ if (uid == 0 && (root_uses_machine_creds == 1 || ++ service != NULL)) { + int nocache = 0; + int success = 0; + do { + gssd_refresh_krb5_machine_credential(clp->servername, +- NULL, nocache); ++ NULL, service); + /* + * Get a list of credential cache names and try each + * of them until one works or we've tried them all +@@ -904,7 +1018,7 @@ handle_krb5_upcall(struct clnt_info *clp + goto out_return_error; + } + +- do_downcall(clp->krb5_fd, uid, &pd, &token); ++ do_downcall(fd, uid, &pd, &token); + + out: + if (token.value) +@@ -920,7 +1034,7 @@ out: + return; + + out_return_error: +- do_error_downcall(clp->krb5_fd, uid, -1); ++ do_error_downcall(fd, uid, -1); + goto out; + } + +@@ -928,26 +1042,19 @@ out_return_error: + * this code uses the userland rpcsec gss library to create an spkm3 + * context on behalf of the kernel + */ +-void +-handle_spkm3_upcall(struct clnt_info *clp) ++static void ++process_spkm3_upcall(struct clnt_info *clp, uid_t uid, int fd) + { +- uid_t uid; + CLIENT *rpc_clnt = NULL; + AUTH *auth = NULL; + struct authgss_private_data pd; + gss_buffer_desc token; + +- printerr(2, "handling spkm3 upcall\n"); ++ printerr(2, "handling spkm3 upcall (%s)\n", clp->dirname); + + token.length = 0; + token.value = NULL; + +- if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) { +- printerr(0, "WARNING: failed reading uid from spkm3 " +- "upcall pipe: %s\n", strerror(errno)); +- goto out; +- } +- + if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) { + printerr(0, "WARNING: Failed to create spkm3 context for " + "user with uid %d\n", uid); +@@ -968,7 +1075,7 @@ handle_spkm3_upcall(struct clnt_info *cl + goto out_return_error; + } + +- do_downcall(clp->spkm3_fd, uid, &pd, &token); ++ do_downcall(fd, uid, &pd, &token); + + out: + if (token.value) +@@ -980,6 +1087,139 @@ out: + return; + + out_return_error: +- do_error_downcall(clp->spkm3_fd, uid, -1); ++ do_error_downcall(fd, uid, -1); + goto out; + } ++ ++void ++handle_krb5_upcall(struct clnt_info *clp) ++{ ++ uid_t uid; ++ ++ if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) { ++ printerr(0, "WARNING: failed reading uid from krb5 " ++ "upcall pipe: %s\n", strerror(errno)); ++ return; ++ } ++ ++ return process_krb5_upcall(clp, uid, clp->krb5_fd, NULL, NULL); ++} ++ ++void ++handle_spkm3_upcall(struct clnt_info *clp) ++{ ++ uid_t uid; ++ ++ if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) { ++ printerr(0, "WARNING: failed reading uid from spkm3 " ++ "upcall pipe: %s\n", strerror(errno)); ++ return; ++ } ++ ++ return process_spkm3_upcall(clp, uid, clp->spkm3_fd); ++} ++ ++void ++handle_gssd_upcall(struct clnt_info *clp) ++{ ++ uid_t uid; ++ char *lbuf = NULL; ++ int lbuflen = 0; ++ char *p; ++ char *mech = NULL; ++ char *target = NULL; ++ char *service = NULL; ++ ++ printerr(1, "handling gssd upcall (%s)\n", clp->dirname); ++ ++ if (readline(clp->gssd_fd, &lbuf, &lbuflen) != 1) { ++ printerr(0, "WARNING: handle_gssd_upcall: " ++ "failed reading request\n"); ++ return; ++ } ++ printerr(2, "%s: '%s'\n", __func__, lbuf); ++ ++ /* find the mechanism name */ ++ if ((p = strstr(lbuf, "mech=")) != NULL) { ++ mech = malloc(lbuflen); ++ if (!mech) ++ goto out; ++ if (sscanf(p, "mech=%s", mech) != 1) { ++ printerr(0, "WARNING: handle_gssd_upcall: " ++ "failed to parse gss mechanism name " ++ "in upcall string '%s'\n", lbuf); ++ goto out; ++ } ++ } else { ++ printerr(0, "WARNING: handle_gssd_upcall: " ++ "failed to find gss mechanism name " ++ "in upcall string '%s'\n", lbuf); ++ goto out; ++ } ++ ++ /* read uid */ ++ if ((p = strstr(lbuf, "uid=")) != NULL) { ++ if (sscanf(p, "uid=%d", &uid) != 1) { ++ printerr(0, "WARNING: handle_gssd_upcall: " ++ "failed to parse uid " ++ "in upcall string '%s'\n", lbuf); ++ goto out; ++ } ++ } else { ++ printerr(0, "WARNING: handle_gssd_upcall: " ++ "failed to find uid " ++ "in upcall string '%s'\n", lbuf); ++ goto out; ++ } ++ ++ /* read target name */ ++ if ((p = strstr(lbuf, "target=")) != NULL) { ++ target = malloc(lbuflen); ++ if (!target) ++ goto out; ++ if (sscanf(p, "target=%s", target) != 1) { ++ printerr(0, "WARNING: handle_gssd_upcall: " ++ "failed to parse target name " ++ "in upcall string '%s'\n", lbuf); ++ goto out; ++ } ++ } ++ ++ /* ++ * read the service name ++ * ++ * The presence of attribute "service=" indicates that machine ++ * credentials should be used for this request. If the value ++ * is "*", then any machine credentials available can be used. ++ * If the value is anything else, then machine credentials for ++ * the specified service name (always "nfs" for now) should be ++ * used. ++ */ ++ if ((p = strstr(lbuf, "service=")) != NULL) { ++ service = malloc(lbuflen); ++ if (!service) ++ goto out; ++ if (sscanf(p, "service=%s", service) != 1) { ++ printerr(0, "WARNING: handle_gssd_upcall: " ++ "failed to parse service type " ++ "in upcall string '%s'\n", lbuf); ++ goto out; ++ } ++ } ++ ++ if (strcmp(mech, "krb5") == 0) ++ process_krb5_upcall(clp, uid, clp->gssd_fd, target, service); ++ else if (strcmp(mech, "spkm3") == 0) ++ process_spkm3_upcall(clp, uid, clp->gssd_fd); ++ else ++ printerr(0, "WARNING: handle_gssd_upcall: " ++ "received unknown gss mech '%s'\n", mech); ++ ++out: ++ free(lbuf); ++ free(mech); ++ free(target); ++ free(service); ++ return; ++} ++ +diff -up nfs-utils-1.2.1/utils/gssd/krb5_util.c.orig nfs-utils-1.2.1/utils/gssd/krb5_util.c +--- nfs-utils-1.2.1/utils/gssd/krb5_util.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/gssd/krb5_util.c 2010-01-12 06:07:40.770878533 -0500 +@@ -797,10 +797,9 @@ gssd_search_krb5_keytab(krb5_context con + */ + static int + find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname, +- krb5_keytab_entry *kte) ++ krb5_keytab_entry *kte, const char **svcnames) + { + krb5_error_code code; +- const char *svcnames[] = { "root", "nfs", "host", NULL }; + char **realmnames = NULL; + char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST]; + int i, j, retval; +@@ -1096,7 +1095,8 @@ gssd_get_krb5_machine_cred_list(char *** + for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) { + if (ple->ccname) { + /* Make sure cred is up-to-date before returning it */ +- retval = gssd_refresh_krb5_machine_credential(NULL, ple, 0); ++ retval = gssd_refresh_krb5_machine_credential(NULL, ple, ++ NULL); + if (retval) + continue; + if (i + 1 > listsize) { +@@ -1186,14 +1186,24 @@ gssd_destroy_krb5_machine_creds(void) + */ + int + gssd_refresh_krb5_machine_credential(char *hostname, +- struct gssd_k5_kt_princ *ple, int nocache) ++ struct gssd_k5_kt_princ *ple, ++ char *service) + { + krb5_error_code code = 0; + krb5_context context; + krb5_keytab kt = NULL;; + int retval = 0; + char *k5err = NULL; ++ const char *svcnames[4] = { "root", "nfs", "host", NULL }; + ++ /* ++ * If a specific service name was specified, use it. ++ * Otherwise, use the default list. ++ */ ++ if (service != NULL && strcmp(service, "*") != 0) { ++ svcnames[0] = service; ++ svcnames[1] = NULL; ++ } + if (hostname == NULL && ple == NULL) + return EINVAL; + +@@ -1216,7 +1226,7 @@ gssd_refresh_krb5_machine_credential(cha + if (ple == NULL) { + krb5_keytab_entry kte; + +- code = find_keytab_entry(context, kt, hostname, &kte); ++ code = find_keytab_entry(context, kt, hostname, &kte, svcnames); + if (code) { + printerr(0, "ERROR: %s: no usable keytab entry found " + "in keytab %s for connection with host %s\n", +@@ -1241,7 +1251,7 @@ gssd_refresh_krb5_machine_credential(cha + goto out; + } + } +- retval = gssd_get_single_krb5_cred(context, kt, ple, nocache); ++ retval = gssd_get_single_krb5_cred(context, kt, ple, 0); + out: + if (kt) + krb5_kt_close(context, kt); +diff -up nfs-utils-1.2.1/utils/gssd/krb5_util.h.orig nfs-utils-1.2.1/utils/gssd/krb5_util.h +--- nfs-utils-1.2.1/utils/gssd/krb5_util.h.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/gssd/krb5_util.h 2010-01-12 06:07:40.771878946 -0500 +@@ -30,7 +30,8 @@ void gssd_free_krb5_machine_cred_list(ch + void gssd_setup_krb5_machine_gss_ccache(char *servername); + void gssd_destroy_krb5_machine_creds(void); + int gssd_refresh_krb5_machine_credential(char *hostname, +- struct gssd_k5_kt_princ *ple, int nocache); ++ struct gssd_k5_kt_princ *ple, ++ char *service); + char *gssd_k5_err_msg(krb5_context context, krb5_error_code code); + void gssd_k5_get_default_realm(char **def_realm); + +diff -up nfs-utils-1.2.1/utils/gssd/svcgssd_proc.c.orig nfs-utils-1.2.1/utils/gssd/svcgssd_proc.c +--- nfs-utils-1.2.1/utils/gssd/svcgssd_proc.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/gssd/svcgssd_proc.c 2010-01-12 06:07:40.772878601 -0500 +@@ -56,6 +56,7 @@ + #include "gss_util.h" + #include "err_util.h" + #include "context.h" ++#include "gss_oids.h" + + extern char * mech2file(gss_OID mech); + #define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.rpcsec.context/channel" +@@ -73,7 +74,7 @@ struct svc_cred { + static int + do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred, + gss_OID mech, gss_buffer_desc *context_token, +- int32_t endtime) ++ int32_t endtime, char *client_name) + { + FILE *f; + int i; +@@ -98,9 +99,10 @@ do_svc_downcall(gss_buffer_desc *out_han + qword_printint(f, cred->cr_gid); + qword_printint(f, cred->cr_ngroups); + printerr(2, "mech: %s, hndl len: %d, ctx len %d, timeout: %d (%d from now), " +- "uid: %d, gid: %d, num aux grps: %d:\n", ++ "clnt: %s, uid: %d, gid: %d, num aux grps: %d:\n", + fname, out_handle->length, context_token->length, + endtime, endtime - time(0), ++ client_name ? client_name : "", + cred->cr_uid, cred->cr_gid, cred->cr_ngroups); + for (i=0; i < cred->cr_ngroups; i++) { + qword_printint(f, cred->cr_groups[i]); +@@ -108,6 +110,8 @@ do_svc_downcall(gss_buffer_desc *out_han + } + qword_print(f, fname); + qword_printhex(f, context_token->value, context_token->length); ++ if (client_name) ++ qword_print(f, client_name); + err = qword_eol(f); + if (err) { + printerr(1, "WARNING: error writing to downcall channel " +@@ -307,6 +311,75 @@ print_hexl(const char *description, unsi + } + #endif + ++static int ++get_krb5_hostbased_name (gss_buffer_desc *name, char **hostbased_name) ++{ ++ char *p, *sname = NULL; ++ if (strchr(name->value, '@') && strchr(name->value, '/')) { ++ if ((sname = calloc(name->length, 1)) == NULL) { ++ printerr(0, "ERROR: get_krb5_hostbased_name failed " ++ "to allocate %d bytes\n", name->length); ++ return -1; ++ } ++ /* read in name and instance and replace '/' with '@' */ ++ sscanf(name->value, "%[^@]", sname); ++ p = strrchr(sname, '/'); ++ if (p == NULL) { /* The '@' preceeded the '/' */ ++ free(sname); ++ return -1; ++ } ++ *p = '@'; ++ } ++ *hostbased_name = sname; ++ return 0; ++} ++ ++static int ++get_hostbased_client_name(gss_name_t client_name, gss_OID mech, ++ char **hostbased_name) ++{ ++ u_int32_t maj_stat, min_stat; ++ gss_buffer_desc name; ++ gss_OID name_type = GSS_C_NO_OID; ++ char *cname; ++ int res = -1; ++ ++ *hostbased_name = NULL; /* preset in case we fail */ ++ ++ /* Get the client's gss authenticated name */ ++ maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type); ++ if (maj_stat != GSS_S_COMPLETE) { ++ pgsserr("get_hostbased_client_name: gss_display_name", ++ maj_stat, min_stat, mech); ++ goto out_err; ++ } ++ if (name.length >= 0xffff) { /* don't overflow */ ++ printerr(0, "ERROR: get_hostbased_client_name: " ++ "received gss_name is too long (%d bytes)\n", ++ name.length); ++ goto out_rel_buf; ++ } ++ ++ /* For Kerberos, transform the NT_KRB5_PRINCIPAL name to ++ * an NT_HOSTBASED_SERVICE name */ ++ if (g_OID_equal(&krb5oid, mech)) { ++ if (get_krb5_hostbased_name(&name, &cname) == 0) ++ *hostbased_name = cname; ++ } ++ ++ /* No support for SPKM3, just print a warning (for now) */ ++ if (g_OID_equal(&spkm3oid, mech)) { ++ printerr(1, "WARNING: get_hostbased_client_name: " ++ "no hostbased_name support for SPKM3\n"); ++ } ++ ++ res = 0; ++out_rel_buf: ++ gss_release_buffer(&min_stat, &name); ++out_err: ++ return res; ++} ++ + void + handle_nullreq(FILE *f) { + /* XXX initialize to a random integer to reduce chances of unnecessary +@@ -325,7 +398,7 @@ handle_nullreq(FILE *f) { + null_token = {.value = NULL}; + u_int32_t ret_flags; + gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; +- gss_name_t client_name; ++ gss_name_t client_name = NULL; + gss_OID mech = GSS_C_NO_OID; + u_int32_t maj_stat = GSS_S_FAILURE, min_stat = 0; + u_int32_t ignore_min_stat; +@@ -334,6 +407,7 @@ handle_nullreq(FILE *f) { + static int lbuflen = 0; + static char *cp; + int32_t ctx_endtime; ++ char *hostbased_name = NULL; + + printerr(1, "handling null request\n"); + +@@ -396,11 +470,13 @@ handle_nullreq(FILE *f) { + if (get_ids(client_name, mech, &cred)) { + /* get_ids() prints error msg */ + maj_stat = GSS_S_BAD_NAME; /* XXX ? */ +- gss_release_name(&ignore_min_stat, &client_name); + goto out_err; + } +- gss_release_name(&ignore_min_stat, &client_name); +- ++ if (get_hostbased_client_name(client_name, mech, &hostbased_name)) { ++ /* get_hostbased_client_name() prints error msg */ ++ maj_stat = GSS_S_BAD_NAME; /* XXX ? */ ++ goto out_err; ++ } + + /* Context complete. Pass handle_seq in out_handle to use + * for context lookup in the kernel. */ +@@ -419,7 +495,8 @@ handle_nullreq(FILE *f) { + /* We no longer need the gss context */ + gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok); + +- do_svc_downcall(&out_handle, &cred, mech, &ctx_token, ctx_endtime); ++ do_svc_downcall(&out_handle, &cred, mech, &ctx_token, ctx_endtime, ++ hostbased_name); + continue_needed: + send_response(f, &in_handle, &in_tok, maj_stat, min_stat, + &out_handle, &out_tok); +@@ -428,6 +505,9 @@ out: + free(ctx_token.value); + if (out_tok.value != NULL) + gss_release_buffer(&ignore_min_stat, &out_tok); ++ if (client_name) ++ gss_release_name(&ignore_min_stat, &client_name); ++ free(hostbased_name); + printerr(1, "finished handling null request\n"); + return; + +diff -up nfs-utils-1.2.1/utils/mountd/auth.c.orig nfs-utils-1.2.1/utils/mountd/auth.c +--- nfs-utils-1.2.1/utils/mountd/auth.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/mountd/auth.c 2010-01-12 06:07:40.779888687 -0500 +@@ -169,8 +169,7 @@ auth_authenticate_internal(char *what, s + } + } + if (!(exp->m_export.e_flags & NFSEXP_INSECURE_PORT) && +- (ntohs(caller->sin_port) < IPPORT_RESERVED/2 || +- ntohs(caller->sin_port) >= IPPORT_RESERVED)) { ++ ntohs(caller->sin_port) >= IPPORT_RESERVED) { + *error = illegal_port; + return NULL; + } +diff -up nfs-utils-1.2.1/utils/mount/mount.c.orig nfs-utils-1.2.1/utils/mount/mount.c +--- nfs-utils-1.2.1/utils/mount/mount.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/mount/mount.c 2010-01-12 06:07:40.772878601 -0500 +@@ -593,6 +593,9 @@ int main(int argc, char *argv[]) + if (mnt_err == EX_BG) { + printf(_("%s: backgrounding \"%s\"\n"), + progname, spec); ++ printf(_("%s: mount options: \"%s\"\n"), ++ progname, extra_opts); ++ + fflush(stdout); + + /* +diff -up nfs-utils-1.2.1/utils/mount/network.c.orig nfs-utils-1.2.1/utils/mount/network.c +--- nfs-utils-1.2.1/utils/mount/network.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/mount/network.c 2010-01-12 06:07:40.773889180 -0500 +@@ -193,8 +193,18 @@ static const unsigned int *nfs_default_p + } + #endif /* MOUNT_CONFIG */ + +-static int nfs_lookup(const char *hostname, const sa_family_t family, +- struct sockaddr *sap, socklen_t *salen) ++/** ++ * nfs_lookup - resolve hostname to an IPv4 or IPv6 socket address ++ * @hostname: pointer to C string containing DNS hostname to resolve ++ * @family: address family hint ++ * @sap: pointer to buffer to fill with socket address ++ * @len: IN: size of buffer to fill; OUT: size of socket address ++ * ++ * Returns 1 and places a socket address at @sap if successful; ++ * otherwise zero. ++ */ ++int nfs_lookup(const char *hostname, const sa_family_t family, ++ struct sockaddr *sap, socklen_t *salen) + { + struct addrinfo *gai_results; + struct addrinfo gai_hint = { +@@ -243,25 +253,6 @@ static int nfs_lookup(const char *hostna + } + + /** +- * nfs_name_to_address - resolve hostname to an IPv4 or IPv6 socket address +- * @hostname: pointer to C string containing DNS hostname to resolve +- * @sap: pointer to buffer to fill with socket address +- * @len: IN: size of buffer to fill; OUT: size of socket address +- * +- * Returns 1 and places a socket address at @sap if successful; +- * otherwise zero. +- */ +-int nfs_name_to_address(const char *hostname, +- struct sockaddr *sap, socklen_t *salen) +-{ +-#ifdef IPV6_SUPPORTED +- return nfs_lookup(hostname, AF_UNSPEC, sap, salen); +-#else /* !IPV6_SUPPORTED */ +- return nfs_lookup(hostname, AF_INET, sap, salen); +-#endif /* !IPV6_SUPPORTED */ +-} +- +-/** + * nfs_gethostbyname - resolve a hostname to an IPv4 address + * @hostname: pointer to a C string containing a DNS hostname + * @sin: returns an IPv4 address +@@ -283,8 +274,8 @@ int nfs_gethostbyname(const char *hostna + * OUT: length of converted socket address + * + * Convert a presentation format address string to a socket address. +- * Similar to nfs_name_to_address(), but the DNS query is squelched, +- * and won't make any noise if the getaddrinfo() call fails. ++ * Similar to nfs_lookup(), but the DNS query is squelched, and it ++ * won't make any noise if the getaddrinfo() call fails. + * + * Returns 1 and fills in @sap and @salen if successful; otherwise zero. + * +@@ -1289,6 +1280,7 @@ nfs_nfs_version(struct mount_options *op + int + nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol) + { ++ sa_family_t family; + char *option; + + switch (po_rightmost(options, nfs_transport_opttbl)) { +@@ -1300,17 +1292,8 @@ nfs_nfs_protocol(struct mount_options *o + return 1; + case 2: /* proto */ + option = po_get(options, "proto"); +- if (option) { +- if (strcmp(option, "tcp") == 0) { +- *protocol = IPPROTO_TCP; +- return 1; +- } +- if (strcmp(option, "udp") == 0) { +- *protocol = IPPROTO_UDP; +- return 1; +- } +- return 0; +- } ++ if (option != NULL) ++ return nfs_get_proto(option, &family, protocol); + } + + /* +@@ -1352,6 +1335,40 @@ nfs_nfs_port(struct mount_options *optio + } + + /* ++ * Returns TRUE and fills in @family if a valid NFS protocol option ++ * is found, or FALSE if the option was specified with an invalid value. ++ */ ++int nfs_nfs_proto_family(struct mount_options *options, ++ sa_family_t *family) ++{ ++ unsigned long protocol; ++ char *option; ++ ++#ifdef IPV6_SUPPORTED ++ *family = AF_UNSPEC; ++#else ++ *family = AF_INET; ++#endif ++ ++ switch (po_rightmost(options, nfs_transport_opttbl)) { ++ case 0: /* udp */ ++ return 1; ++ case 1: /* tcp */ ++ return 1; ++ case 2: /* proto */ ++ option = po_get(options, "proto"); ++ if (option != NULL) ++ return nfs_get_proto(option, family, &protocol); ++ } ++ ++ /* ++ * NFS transport protocol wasn't specified. Return the ++ * default address family. ++ */ ++ return 1; ++} ++ ++/* + * "mountprog" is supported only by the legacy mount command. The + * kernel mount client does not support this option. + * +@@ -1419,20 +1436,12 @@ nfs_mount_version(struct mount_options * + static int + nfs_mount_protocol(struct mount_options *options, unsigned long *protocol) + { ++ sa_family_t family; + char *option; + + option = po_get(options, "mountproto"); +- if (option) { +- if (strcmp(option, "tcp") == 0) { +- *protocol = IPPROTO_TCP; +- return 1; +- } +- if (strcmp(option, "udp") == 0) { +- *protocol = IPPROTO_UDP; +- return 1; +- } +- return 0; +- } ++ if (option != NULL) ++ return nfs_get_proto(option, &family, protocol); + + /* + * MNT transport protocol wasn't specified. If the NFS +@@ -1472,6 +1481,35 @@ nfs_mount_port(struct mount_options *opt + return 1; + } + ++/* ++ * Returns TRUE and fills in @family if a valid MNT protocol option ++ * is found, or FALSE if the option was specified with an invalid value. ++ */ ++int nfs_mount_proto_family(struct mount_options *options, ++ sa_family_t *family) ++{ ++ unsigned long protocol; ++ char *option; ++ ++#ifdef HAVE_LIBTIRPC ++ *family = AF_UNSPEC; ++#else ++ *family = AF_INET; ++#endif ++ ++ option = po_get(options, "mountproto"); ++ if (option != NULL) ++ return nfs_get_proto(option, family, &protocol); ++ ++ /* ++ * MNT transport protocol wasn't specified. If the NFS ++ * transport protocol was specified, derive the family ++ * from that; otherwise, return the default family for ++ * NFS. ++ */ ++ return nfs_nfs_proto_family(options, family); ++} ++ + /** + * nfs_options2pmap - set up pmap structs based on mount options + * @options: pointer to mount options +diff -up nfs-utils-1.2.1/utils/mount/network.h.orig nfs-utils-1.2.1/utils/mount/network.h +--- nfs-utils-1.2.1/utils/mount/network.h.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/mount/network.h 2010-01-12 06:07:40.774878676 -0500 +@@ -44,7 +44,8 @@ int nfs_probe_bothports(const struct soc + struct pmap *, const struct sockaddr *, + const socklen_t, struct pmap *); + int nfs_gethostbyname(const char *, struct sockaddr_in *); +-int nfs_name_to_address(const char *, struct sockaddr *, socklen_t *); ++int nfs_lookup(const char *hostname, const sa_family_t family, ++ struct sockaddr *sap, socklen_t *salen); + int nfs_string_to_sockaddr(const char *, struct sockaddr *, socklen_t *); + int nfs_present_sockaddr(const struct sockaddr *, + const socklen_t, char *, const size_t); +@@ -56,6 +57,8 @@ int clnt_ping(struct sockaddr_in *, cons + + struct mount_options; + ++int nfs_nfs_proto_family(struct mount_options *options, sa_family_t *family); ++int nfs_mount_proto_family(struct mount_options *options, sa_family_t *family); + int nfs_nfs_version(struct mount_options *options, unsigned long *version); + int nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol); + +diff -up nfs-utils-1.2.1/utils/mount/nfs4mount.c.orig nfs-utils-1.2.1/utils/mount/nfs4mount.c +--- nfs-utils-1.2.1/utils/mount/nfs4mount.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/mount/nfs4mount.c 2010-01-12 06:07:40.776889340 -0500 +@@ -217,8 +217,11 @@ int nfs4mount(const char *spec, const ch + progname); + goto fail; + } +- snprintf(new_opts, sizeof(new_opts), "%s%saddr=%s", +- old_opts, *old_opts ? "," : "", s); ++ if (running_bg) ++ strncpy(new_opts, old_opts, sizeof(new_opts)); ++ else ++ snprintf(new_opts, sizeof(new_opts), "%s%saddr=%s", ++ old_opts, *old_opts ? "," : "", s); + *extra_opts = xstrdup(new_opts); + + /* Set default options. +@@ -434,15 +437,17 @@ int nfs4mount(const char *spec, const ch + break; + } + +- switch(rpc_createerr.cf_stat){ +- case RPC_TIMEDOUT: +- break; +- case RPC_SYSTEMERROR: +- if (errno == ETIMEDOUT) ++ if (!bg) { ++ switch(rpc_createerr.cf_stat) { ++ case RPC_TIMEDOUT: + break; +- default: +- rpc_mount_errors(hostname, 0, bg); +- goto fail; ++ case RPC_SYSTEMERROR: ++ if (errno == ETIMEDOUT) ++ break; ++ default: ++ rpc_mount_errors(hostname, 0, bg); ++ goto fail; ++ } + } + + if (bg && !running_bg) { +diff -up nfs-utils-1.2.1/utils/mount/nfs.man.orig nfs-utils-1.2.1/utils/mount/nfs.man +--- nfs-utils-1.2.1/utils/mount/nfs.man.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/mount/nfs.man 2010-01-12 06:07:40.775888996 -0500 +@@ -58,9 +58,17 @@ The server's hostname and export pathnam + are separated by a colon, while + the mount options are separated by commas. The remaining fields + are separated by blanks or tabs. ++.P + The server's hostname can be an unqualified hostname, + a fully qualified domain name, +-or a dotted quad IPv4 address. ++a dotted quad IPv4 address, or ++an IPv6 address enclosed in square brackets. ++Link-local and site-local IPv6 addresses must be accompanied by an ++interface identifier. ++See ++.BR ipv6 (7) ++for details on specifying raw IPv6 addresses. ++.P + The + .I fstype + field contains either "nfs" (for version 2 or version 3 NFS mounts) +@@ -470,32 +478,38 @@ for mounting the + .B nfs + file system type. + .TP 1.5i +-.BI proto= transport +-The transport the NFS client uses ++.BI proto= netid ++The transport protocol name and protocol family the NFS client uses + to transmit requests to the NFS server for this mount point. +-.I transport +-can be either +-.B udp +-or +-.BR tcp . +-Each transport uses different default ++If an NFS server has both an IPv4 and an IPv6 address, using a specific ++netid will force the use of IPv4 or IPv6 networking to communicate ++with that server. ++.IP ++If support for TI-RPC is built into the ++.B mount.nfs ++command, ++.I netid ++is a valid netid listed in ++.IR /etc/netconfig . ++Otherwise, ++.I netid ++is one of "tcp," "udp," or "rdma," and only IPv4 may be used. ++.IP ++Each transport protocol uses different default + .B retrans + and + .B timeo +-settings; refer to the description of these two mount options for details. ++settings. ++Refer to the description of these two mount options for details. + .IP + In addition to controlling how the NFS client transmits requests to + the server, this mount option also controls how the + .BR mount (8) + command communicates with the server's rpcbind and mountd services. +-Specifying +-.B proto=tcp +-forces all traffic from the ++Specifying a netid that uses TCP forces all traffic from the + .BR mount (8) + command and the NFS client to use TCP. +-Specifying +-.B proto=udp +-forces all traffic types to use UDP. ++Specifying a netid that uses UDP forces all traffic types to use UDP. + .IP + If the + .B proto +@@ -548,15 +562,20 @@ or the server's mountd service is not av + This option can be used when mounting an NFS server + through a firewall that blocks the rpcbind protocol. + .TP 1.5i +-.BI mountproto= transport +-The transport the NFS client uses ++.BI mountproto= netid ++The transport protocol name and protocol family the NFS client uses + to transmit requests to the NFS server's mountd service when performing + this mount request, and when later unmounting this mount point. +-.I transport +-can be either +-.B udp +-or +-.BR tcp . ++.IP ++If support for TI-RPC is built into the ++.B mount.nfs ++command, ++.I netid ++is a valid netid listed in ++.IR /etc/netconfig . ++Otherwise, ++.I netid ++is one of "tcp" or "udp," and only IPv4 may be used. + .IP + This option can be used when mounting an NFS server + through a firewall that blocks a particular transport. +@@ -566,6 +585,7 @@ option, different transports for mountd + can be specified. + If the server's mountd service is not available via the specified + transport, the mount request fails. ++.IP + Refer to the TRANSPORT METHODS section for more on how the + .B mountproto + mount option interacts with the +@@ -709,17 +729,26 @@ for mounting the + .B nfs4 + file system type. + .TP 1.5i +-.BI proto= transport +-The transport the NFS client uses ++.BI proto= netid ++The transport protocol name and protocol family the NFS client uses + to transmit requests to the NFS server for this mount point. +-.I transport +-can be either +-.B udp +-or +-.BR tcp . ++If an NFS server has both an IPv4 and an IPv6 address, using a specific ++netid will force the use of IPv4 or IPv6 networking to communicate ++with that server. ++.IP ++If support for TI-RPC is built into the ++.B mount.nfs ++command, ++.I netid ++is a valid netid listed in ++.IR /etc/netconfig . ++Otherwise, ++.I netid ++is one of "tcp" or "udp," and only IPv4 may be used. ++.IP + All NFS version 4 servers are required to support TCP, + so if this mount option is not specified, the NFS version 4 client +-uses the TCP transport protocol. ++uses the TCP protocol. + Refer to the TRANSPORT METHODS section for more details. + .TP 1.5i + .BI port= n +@@ -779,7 +808,8 @@ The DATA AND METADATA COHERENCE section + the behavior of this option in more detail. + .TP 1.5i + .BI clientaddr= n.n.n.n +-Specifies a single IPv4 address (in dotted-quad form) ++Specifies a single IPv4 address (in dotted-quad form), ++or a non-link-local IPv6 address, + that the NFS client advertises to allow servers + to perform NFS version 4 callback requests against + files on this mount point. If the server is unable to +@@ -855,6 +885,14 @@ This example can be used to mount /usr o + .TA 2.5i +0.7i +0.7i +.7i + server:/export /usr nfs ro,nolock,nocto,actimeo=3600 0 0 + .FI ++.P ++This example shows how to mount an NFS server ++using a raw IPv6 link-local address. ++.P ++.NF ++.TA 2.5i +0.7i +0.7i +.7i ++ [fe80::215:c5ff:fb3e:e2b1%eth0]:/export /mnt nfs defaults 0 0 ++.FI + .SH "TRANSPORT METHODS" + NFS clients send requests to NFS servers via + Remote Procedure Calls, or +@@ -1498,6 +1536,8 @@ such as security negotiation, server ref + .BR mount.nfs (5), + .BR umount.nfs (5), + .BR exports (5), ++.BR netconfig (5), ++.BR ipv6 (7), + .BR nfsd (8), + .BR sm-notify (8), + .BR rpc.statd (8), +diff -up nfs-utils-1.2.1/utils/mount/nfsmount.c.orig nfs-utils-1.2.1/utils/mount/nfsmount.c +--- nfs-utils-1.2.1/utils/mount/nfsmount.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/mount/nfsmount.c 2010-01-12 06:07:40.777878312 -0500 +@@ -170,7 +170,7 @@ parse_options(char *old_opts, struct nfs + struct pmap *mnt_pmap = &mnt_server->pmap; + struct pmap *nfs_pmap = &nfs_server->pmap; + int len; +- char *opt, *opteq, *p, *opt_b; ++ char *opt, *opteq, *p, *opt_b, *tmp_opts; + char *mounthost = NULL; + char cbuf[128]; + int open_quote = 0; +@@ -179,7 +179,8 @@ parse_options(char *old_opts, struct nfs + *bg = 0; + + len = strlen(new_opts); +- for (p=old_opts, opt_b=NULL; p && *p; p++) { ++ tmp_opts = xstrdup(old_opts); ++ for (p=tmp_opts, opt_b=NULL; p && *p; p++) { + if (!opt_b) + opt_b = p; /* begin of the option item */ + if (*p == '"') +@@ -457,10 +458,12 @@ parse_options(char *old_opts, struct nfs + goto out_bad; + *mnt_server->hostname = mounthost; + } ++ free(tmp_opts); + return 1; + bad_parameter: + nfs_error(_("%s: Bad nfs mount parameter: %s\n"), progname, opt); + out_bad: ++ free(tmp_opts); + return 0; + } + +diff -up nfs-utils-1.2.1/utils/mount/nfsumount.c.orig nfs-utils-1.2.1/utils/mount/nfsumount.c +--- nfs-utils-1.2.1/utils/mount/nfsumount.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/mount/nfsumount.c 2010-01-12 06:07:40.778899357 -0500 +@@ -169,10 +169,15 @@ out: + static int nfs_umount_do_umnt(struct mount_options *options, + char **hostname, char **dirname) + { +- struct sockaddr_storage address; +- struct sockaddr *sap = (struct sockaddr *)&address; ++ union { ++ struct sockaddr sa; ++ struct sockaddr_in s4; ++ struct sockaddr_in6 s6; ++ } address; ++ struct sockaddr *sap = &address.sa; + socklen_t salen = sizeof(address); + struct pmap nfs_pmap, mnt_pmap; ++ sa_family_t family; + + if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap)) { + nfs_error(_("%s: bad mount options"), progname); +@@ -189,8 +194,10 @@ static int nfs_umount_do_umnt(struct mou + return EX_FAIL; + } + +- if (nfs_name_to_address(*hostname, sap, &salen) == 0) +- /* nfs_name_to_address reports any errors */ ++ if (!nfs_mount_proto_family(options, &family)) ++ return 0; ++ if (!nfs_lookup(*hostname, family, sap, &salen)) ++ /* nfs_lookup reports any errors */ + return EX_FAIL; + + if (nfs_advise_umount(sap, salen, &mnt_pmap, dirname) == 0) +diff -up nfs-utils-1.2.1/utils/mount/stropts.c.orig nfs-utils-1.2.1/utils/mount/stropts.c +--- nfs-utils-1.2.1/utils/mount/stropts.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/mount/stropts.c 2010-01-12 06:07:40.779888687 -0500 +@@ -38,6 +38,7 @@ + #include "xcommon.h" + #include "mount.h" + #include "nls.h" ++#include "nfsrpc.h" + #include "mount_constants.h" + #include "stropts.h" + #include "error.h" +@@ -76,12 +77,18 @@ extern char *progname; + extern int verbose; + extern int sloppy; + ++union nfs_sockaddr { ++ struct sockaddr sa; ++ struct sockaddr_in s4; ++ struct sockaddr_in6 s6; ++}; ++ + struct nfsmount_info { + const char *spec, /* server:/path */ + *node, /* mounted-on dir */ + *type; /* "nfs" or "nfs4" */ + char *hostname; /* server's hostname */ +- struct sockaddr_storage address; /* server's address */ ++ union nfs_sockaddr address; + socklen_t salen; /* size of server's address */ + + struct mount_options *options; /* parsed mount options */ +@@ -204,9 +211,9 @@ static int nfs_append_clientaddr_option( + socklen_t salen, + struct mount_options *options) + { +- struct sockaddr_storage dummy; +- struct sockaddr *my_addr = (struct sockaddr *)&dummy; +- socklen_t my_len = sizeof(dummy); ++ union nfs_sockaddr address; ++ struct sockaddr *my_addr = &address.sa; ++ socklen_t my_len = sizeof(address); + + if (po_contains(options, "clientaddr") == PO_FOUND) + return 1; +@@ -218,21 +225,33 @@ static int nfs_append_clientaddr_option( + } + + /* +- * Resolve the 'mounthost=' hostname and append a new option using +- * the resulting address. ++ * Determine whether to append a 'mountaddr=' option. The option is needed if: ++ * ++ * 1. "mounthost=" was specified, or ++ * 2. The address families for proto= and mountproto= are different. + */ +-static int nfs_fix_mounthost_option(struct mount_options *options) ++static int nfs_fix_mounthost_option(struct mount_options *options, ++ const char *nfs_hostname) + { +- struct sockaddr_storage dummy; +- struct sockaddr *sap = (struct sockaddr *)&dummy; +- socklen_t salen = sizeof(dummy); ++ union nfs_sockaddr address; ++ struct sockaddr *sap = &address.sa; ++ socklen_t salen = sizeof(address); ++ sa_family_t nfs_family, mnt_family; + char *mounthost; + ++ if (!nfs_nfs_proto_family(options, &nfs_family)) ++ return 0; ++ if (!nfs_mount_proto_family(options, &mnt_family)) ++ return 0; ++ + mounthost = po_get(options, "mounthost"); +- if (!mounthost) +- return 1; ++ if (mounthost == NULL) { ++ if (nfs_family == mnt_family) ++ return 1; ++ mounthost = (char *)nfs_hostname; ++ } + +- if (!nfs_name_to_address(mounthost, sap, &salen)) { ++ if (!nfs_lookup(mounthost, mnt_family, sap, &salen)) { + nfs_error(_("%s: unable to determine mount server's address"), + progname); + return 0; +@@ -319,13 +338,16 @@ static int nfs_set_version(struct nfsmou + */ + static int nfs_validate_options(struct nfsmount_info *mi) + { +- struct sockaddr *sap = (struct sockaddr *)&mi->address; ++ struct sockaddr *sap = &mi->address.sa; ++ sa_family_t family; + + if (!nfs_parse_devname(mi->spec, &mi->hostname, NULL)) + return 0; + ++ if (!nfs_nfs_proto_family(mi->options, &family)) ++ return 0; + mi->salen = sizeof(mi->address); +- if (!nfs_name_to_address(mi->hostname, sap, &mi->salen)) ++ if (!nfs_lookup(mi->hostname, family, sap, &mi->salen)) + return 0; + + if (!nfs_set_version(mi)) +@@ -371,10 +393,13 @@ static int nfs_extract_server_addresses( + } + + static int nfs_construct_new_options(struct mount_options *options, ++ struct sockaddr *nfs_saddr, + struct pmap *nfs_pmap, ++ struct sockaddr *mnt_saddr, + struct pmap *mnt_pmap) + { + char new_option[64]; ++ char *netid; + + po_remove_all(options, "nfsprog"); + po_remove_all(options, "mountprog"); +@@ -391,20 +416,14 @@ static int nfs_construct_new_options(str + po_remove_all(options, "proto"); + po_remove_all(options, "udp"); + po_remove_all(options, "tcp"); +- switch (nfs_pmap->pm_prot) { +- case IPPROTO_TCP: +- snprintf(new_option, sizeof(new_option) - 1, +- "proto=tcp"); +- if (po_append(options, new_option) == PO_FAILED) +- return 0; +- break; +- case IPPROTO_UDP: +- snprintf(new_option, sizeof(new_option) - 1, +- "proto=udp"); +- if (po_append(options, new_option) == PO_FAILED) +- return 0; +- break; +- } ++ netid = nfs_get_netid(nfs_saddr->sa_family, nfs_pmap->pm_prot); ++ if (netid == NULL) ++ return 0; ++ snprintf(new_option, sizeof(new_option) - 1, ++ "proto=%s", netid); ++ free(netid); ++ if (po_append(options, new_option) == PO_FAILED) ++ return 0; + + po_remove_all(options, "port"); + if (nfs_pmap->pm_port != NFS_PORT) { +@@ -421,20 +440,14 @@ static int nfs_construct_new_options(str + return 0; + + po_remove_all(options, "mountproto"); +- switch (mnt_pmap->pm_prot) { +- case IPPROTO_TCP: +- snprintf(new_option, sizeof(new_option) - 1, +- "mountproto=tcp"); +- if (po_append(options, new_option) == PO_FAILED) +- return 0; +- break; +- case IPPROTO_UDP: +- snprintf(new_option, sizeof(new_option) - 1, +- "mountproto=udp"); +- if (po_append(options, new_option) == PO_FAILED) +- return 0; +- break; +- } ++ netid = nfs_get_netid(mnt_saddr->sa_family, mnt_pmap->pm_prot); ++ if (netid == NULL) ++ return 0; ++ snprintf(new_option, sizeof(new_option) - 1, ++ "mountproto=%s", netid); ++ free(netid); ++ if (po_append(options, new_option) == PO_FAILED) ++ return 0; + + po_remove_all(options, "mountport"); + snprintf(new_option, sizeof(new_option) - 1, +@@ -461,12 +474,12 @@ static int nfs_construct_new_options(str + static int + nfs_rewrite_pmap_mount_options(struct mount_options *options) + { +- struct sockaddr_storage nfs_address; +- struct sockaddr *nfs_saddr = (struct sockaddr *)&nfs_address; ++ union nfs_sockaddr nfs_address; ++ struct sockaddr *nfs_saddr = &nfs_address.sa; + socklen_t nfs_salen = sizeof(nfs_address); + struct pmap nfs_pmap; +- struct sockaddr_storage mnt_address; +- struct sockaddr *mnt_saddr = (struct sockaddr *)&mnt_address; ++ union nfs_sockaddr mnt_address; ++ struct sockaddr *mnt_saddr = &mnt_address.sa; + socklen_t mnt_salen = sizeof(mnt_address); + struct pmap mnt_pmap; + char *option; +@@ -510,7 +523,8 @@ nfs_rewrite_pmap_mount_options(struct mo + return 0; + } + +- if (!nfs_construct_new_options(options, &nfs_pmap, &mnt_pmap)) { ++ if (!nfs_construct_new_options(options, nfs_saddr, &nfs_pmap, ++ mnt_saddr, &mnt_pmap)) { + errno = EINVAL; + return 0; + } +@@ -566,7 +580,7 @@ static int nfs_try_mount_v3v2(struct nfs + return result; + } + +- if (!nfs_fix_mounthost_option(options)) { ++ if (!nfs_fix_mounthost_option(options, mi->hostname)) { + errno = EINVAL; + goto out_fail; + } +@@ -601,7 +615,7 @@ out_fail: + */ + static int nfs_try_mount_v4(struct nfsmount_info *mi) + { +- struct sockaddr *sap = (struct sockaddr *)&mi->address; ++ struct sockaddr *sap = &mi->address.sa; + struct mount_options *options = po_dup(mi->options); + int result = 0; + +@@ -611,6 +625,18 @@ static int nfs_try_mount_v4(struct nfsmo + } + + if (mi->version == 0) { ++ if (po_contains(options, "mounthost") || ++ po_contains(options, "mountaddr") || ++ po_contains(options, "mountvers") || ++ po_contains(options, "mountproto")) { ++ /* ++ * Since these mountd options are set assume version 3 ++ * is wanted so error out with EPROTONOSUPPORT so the ++ * protocol negation starts with v3. ++ */ ++ errno = EPROTONOSUPPORT; ++ goto out_fail; ++ } + if (po_append(options, "vers=4") == PO_FAILED) { + errno = EINVAL; + goto out_fail; +@@ -656,9 +682,10 @@ static int nfs_try_mount(struct nfsmount + /* + * To deal with legacy Linux servers that don't + * automatically export a pseudo root, retry +- * ENOENT errors using version 3 ++ * ENOENT errors using version 3. And for ++ * Linux servers prior to 2.6.25, retry EPERM + */ +- if (errno != ENOENT) ++ if (errno != ENOENT && errno != EPERM) + break; + } + } +diff -up nfs-utils-1.2.1/utils/nfsd/nfssvc.c.orig nfs-utils-1.2.1/utils/nfsd/nfssvc.c +--- nfs-utils-1.2.1/utils/nfsd/nfssvc.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/nfsd/nfssvc.c 2010-01-12 06:07:40.780888701 -0500 +@@ -212,7 +212,7 @@ int + nfssvc_set_sockets(const int family, const unsigned int protobits, + const char *host, const char *port) + { +- struct addrinfo hints = { .ai_flags = AI_PASSIVE | AI_ADDRCONFIG }; ++ struct addrinfo hints = { .ai_flags = AI_PASSIVE }; + + hints.ai_family = family; + +diff -up nfs-utils-1.2.1/utils/showmount/showmount.c.orig nfs-utils-1.2.1/utils/showmount/showmount.c +--- nfs-utils-1.2.1/utils/showmount/showmount.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/showmount/showmount.c 2010-01-12 06:07:40.781878678 -0500 +@@ -78,29 +78,36 @@ static void usage(FILE *fp, int n) + exit(n); + } + +-static const char *nfs_sm_pgmtbl[] = { ++static const char *mount_pgm_tbl[] = { + "showmount", + "mount", + "mountd", + NULL, + }; + ++static const rpcvers_t mount_vers_tbl[] = { ++ MOUNTVERS_NFSV3, ++ MOUNTVERS_POSIX, ++ MOUNTVERS, ++}; ++static const unsigned int max_vers_tblsz = ++ (sizeof(mount_vers_tbl)/sizeof(mount_vers_tbl[0])); ++ + /* + * Generate an RPC client handle connected to the mountd service + * at @hostname, or die trying. + * + * Supports both AF_INET and AF_INET6 server addresses. + */ +-static CLIENT *nfs_get_mount_client(const char *hostname) ++static CLIENT *nfs_get_mount_client(const char *hostname, rpcvers_t vers) + { +- rpcprog_t program = nfs_getrpcbyname(MOUNTPROG, nfs_sm_pgmtbl); ++ rpcprog_t program = nfs_getrpcbyname(MOUNTPROG, mount_pgm_tbl); + CLIENT *client; + +- client = clnt_create(hostname, program, MOUNTVERS, "tcp"); ++ client = clnt_create(hostname, program, vers, "tcp"); + if (client) + return client; +- +- client = clnt_create(hostname, program, MOUNTVERS, "udp"); ++ client = clnt_create(hostname, program, vers, "udp"); + if (client) + return client; + +@@ -123,6 +130,7 @@ int main(int argc, char **argv) + int i; + int n; + int maxlen; ++ int unsigned vers=0; + char **dumpv; + + program_name = argv[0]; +@@ -185,11 +193,12 @@ int main(int argc, char **argv) + break; + } + +- mclient = nfs_get_mount_client(hostname); ++ mclient = nfs_get_mount_client(hostname, mount_vers_tbl[vers]); + mclient->cl_auth = authunix_create_default(); + total_timeout.tv_sec = TOTAL_TIMEOUT; + total_timeout.tv_usec = 0; + ++again: + if (eflag) { + memset(&exportlist, '\0', sizeof(exportlist)); + +@@ -197,6 +206,13 @@ int main(int argc, char **argv) + (xdrproc_t) xdr_void, NULL, + (xdrproc_t) xdr_exports, (caddr_t) &exportlist, + total_timeout); ++ if (clnt_stat == RPC_PROGVERSMISMATCH) { ++ if (++vers < max_vers_tblsz) { ++ (void)CLNT_CONTROL(mclient, CLSET_VERS, ++ (void *)&mount_vers_tbl[vers]); ++ goto again; ++ } ++ } + if (clnt_stat != RPC_SUCCESS) { + clnt_perror(mclient, "rpc mount export"); + clnt_destroy(mclient); +@@ -232,6 +248,13 @@ int main(int argc, char **argv) + (xdrproc_t) xdr_void, NULL, + (xdrproc_t) xdr_mountlist, (caddr_t) &dumplist, + total_timeout); ++ if (clnt_stat == RPC_PROGVERSMISMATCH) { ++ if (++vers < max_vers_tblsz) { ++ (void)CLNT_CONTROL(mclient, CLSET_VERS, ++ (void *)&mount_vers_tbl[vers]); ++ goto again; ++ } ++ } + if (clnt_stat != RPC_SUCCESS) { + clnt_perror(mclient, "rpc mount dump"); + clnt_destroy(mclient); +diff -up nfs-utils-1.2.1/utils/statd/callback.c.orig nfs-utils-1.2.1/utils/statd/callback.c +--- nfs-utils-1.2.1/utils/statd/callback.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/statd/callback.c 2010-01-12 06:07:40.782830469 -0500 +@@ -35,12 +35,12 @@ sm_notify_1_svc(struct stat_chge *argp, + struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt); + char *ip_addr = xstrdup(inet_ntoa(sin->sin_addr)); + +- dprintf(N_DEBUG, "Received SM_NOTIFY from %s, state: %d", ++ xlog(D_CALL, "Received SM_NOTIFY from %s, state: %d", + argp->mon_name, argp->state); + + /* quick check - don't bother if we're not monitoring anyone */ + if (rtnl == NULL) { +- note(N_WARNING, "SM_NOTIFY from %s while not monitoring any hosts.", ++ xlog_warn("SM_NOTIFY from %s while not monitoring any hosts", + argp->mon_name); + return ((void *) &result); + } +diff -up nfs-utils-1.2.1/utils/statd/Makefile.am.orig nfs-utils-1.2.1/utils/statd/Makefile.am +--- nfs-utils-1.2.1/utils/statd/Makefile.am.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/statd/Makefile.am 2010-01-12 06:07:40.781878678 -0500 +@@ -2,31 +2,26 @@ + + man8_MANS = statd.man sm-notify.man + +-GENFILES_CLNT = sm_inter_clnt.c +-GENFILES_SVC = sm_inter_svc.c +-GENFILES_XDR = sm_inter_xdr.c +-GENFILES_H = sm_inter.h +- +-GENFILES = $(GENFILES_CLNT) $(GENFILES_SVC) $(GENFILES_XDR) $(GENFILES_H) +- + RPCPREFIX = rpc. + KPREFIX = @kprefix@ + sbin_PROGRAMS = statd sm-notify + dist_sbin_SCRIPTS = start-statd +-statd_SOURCES = callback.c notlist.c log.c misc.c monitor.c \ ++statd_SOURCES = callback.c notlist.c misc.c monitor.c \ + simu.c stat.c statd.c svc_run.c rmtcall.c \ +- sm_inter_clnt.c sm_inter_svc.c sm_inter_xdr.c log.h \ +- notlist.h statd.h system.h version.h sm_inter.h ++ notlist.h statd.h system.h version.h + sm_notify_SOURCES = sm-notify.c + + BUILT_SOURCES = $(GENFILES) + statd_LDADD = ../../support/export/libexport.a \ ++ ../../support/nsm/libnsm.a \ + ../../support/nfs/libnfs.a \ + ../../support/misc/libmisc.a \ + $(LIBWRAP) $(LIBNSL) +-sm_notify_LDADD = $(LIBNSL) ++sm_notify_LDADD = ../../support/nsm/libnsm.a \ ++ ../../support/nfs/libnfs.a \ ++ $(LIBNSL) + +-EXTRA_DIST = sim_sm_inter.x sm_inter.x $(man8_MANS) COPYRIGHT simulate.c ++EXTRA_DIST = sim_sm_inter.x $(man8_MANS) COPYRIGHT simulate.c + + if CONFIG_RPCGEN + RPCGEN = $(top_builddir)/tools/rpcgen/rpcgen +diff -up nfs-utils-1.2.1/utils/statd/misc.c.orig nfs-utils-1.2.1/utils/statd/misc.c +--- nfs-utils-1.2.1/utils/statd/misc.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/statd/misc.c 2010-01-12 06:07:40.783888632 -0500 +@@ -29,8 +29,7 @@ xmalloc (size_t size) + return ((void *)NULL); + + if (!(ptr = malloc (size))) +- /* SHIT! SHIT! SHIT! */ +- die ("malloc failed"); ++ xlog_err ("malloc failed"); + + return (ptr); + } +@@ -46,32 +45,7 @@ xstrdup (const char *string) + + /* Will only fail if underlying malloc() fails (ENOMEM). */ + if (!(result = strdup (string))) +- die ("strdup failed"); ++ xlog_err ("strdup failed"); + + return (result); + } +- +- +-/* +- * Unlinking a file. +- */ +-void +-xunlink (char *path, char *host) +-{ +- char *tozap; +- +- tozap = malloc(strlen(path)+strlen(host)+2); +- if (tozap == NULL) { +- note(N_ERROR, "xunlink: malloc failed: errno %d (%s)", +- errno, strerror(errno)); +- return; +- } +- sprintf (tozap, "%s/%s", path, host); +- +- if (unlink (tozap) == -1) +- note(N_ERROR, "unlink (%s): %s", tozap, strerror (errno)); +- else +- dprintf (N_DEBUG, "Unlinked %s", tozap); +- +- free(tozap); +-} +diff -up nfs-utils-1.2.1/utils/statd/monitor.c.orig nfs-utils-1.2.1/utils/statd/monitor.c +--- nfs-utils-1.2.1/utils/statd/monitor.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/statd/monitor.c 2010-01-12 06:07:40.784878513 -0500 +@@ -23,14 +23,13 @@ + + #include "rpcmisc.h" + #include "misc.h" ++#include "nsm.h" + #include "statd.h" + #include "notlist.h" + #include "ha-callout.h" + + notify_list * rtnl = NULL; /* Run-time notify list. */ + +-#define LINELEN (4*(8+1)+SM_PRIV_SIZE*2+1) +- + /* + * Reject requests from non-loopback addresses in order + * to prevent attack described in CERT CA-99.05. +@@ -43,8 +42,7 @@ caller_is_localhost(struct svc_req *rqst + + caller = sin->sin_addr; + if (caller.s_addr != htonl(INADDR_LOOPBACK)) { +- note(N_WARNING, +- "Call to statd from non-local host %s", ++ xlog_warn("Call to statd from non-local host %s", + inet_ntoa(caller)); + return 0; + } +@@ -61,14 +59,17 @@ sm_mon_1_svc(struct mon *argp, struct sv + char *mon_name = argp->mon_id.mon_name, + *my_name = argp->mon_id.my_id.my_name; + struct my_id *id = &argp->mon_id.my_id; +- char *path; + char *cp; +- int fd; + notify_list *clnt; +- struct in_addr my_addr; ++ struct sockaddr_in my_addr = { ++ .sin_family = AF_INET, ++ .sin_addr.s_addr = htonl(INADDR_LOOPBACK), ++ }; + char *dnsname; + struct hostent *hostinfo = NULL; + ++ xlog(D_CALL, "Received SM_MON for %s from %s", mon_name, my_name); ++ + /* Assume that we'll fail. */ + result.res_stat = STAT_FAIL; + result.state = -1; /* State is undefined for STAT_FAIL. */ +@@ -79,7 +80,6 @@ sm_mon_1_svc(struct mon *argp, struct sv + */ + if (!caller_is_localhost(rqstp)) + goto failure; +- my_addr.s_addr = htonl(INADDR_LOOPBACK); + + /* 2. Reject any registrations for non-lockd services. + * +@@ -92,8 +92,7 @@ sm_mon_1_svc(struct mon *argp, struct sv + if (id->my_prog != 100021 || + (id->my_proc != 16 && id->my_proc != 24)) + { +- note(N_WARNING, +- "Attempt to register callback to %d/%d", ++ xlog_warn("Attempt to register callback to %d/%d", + id->my_prog, id->my_proc); + goto failure; + } +@@ -105,12 +104,12 @@ sm_mon_1_svc(struct mon *argp, struct sv + + /* must check for /'s in hostname! See CERT's CA-96.09 for details. */ + if (strchr(mon_name, '/') || mon_name[0] == '.') { +- note(N_CRIT, "SM_MON request for hostname containing '/' " ++ xlog(L_ERROR, "SM_MON request for hostname containing '/' " + "or starting '.': %s", mon_name); +- note(N_CRIT, "POSSIBLE SPOOF/ATTACK ATTEMPT!"); ++ xlog(L_ERROR, "POSSIBLE SPOOF/ATTACK ATTEMPT!"); + goto failure; + } else if ((hostinfo = gethostbyname(mon_name)) == NULL) { +- note(N_WARNING, "gethostbyname error for %s", mon_name); ++ xlog_warn("gethostbyname error for %s", mon_name); + goto failure; + } + +@@ -152,7 +151,7 @@ sm_mon_1_svc(struct mon *argp, struct sv + NL_MY_VERS(clnt) == id->my_vers && + memcmp(NL_PRIV(clnt), argp->priv, SM_PRIV_SIZE) == 0) { + /* Hey! We already know you guys! */ +- dprintf(N_DEBUG, ++ xlog(D_GENERAL, + "Duplicate SM_MON request for %s " + "from procedure on %s", + mon_name, my_name); +@@ -168,11 +167,11 @@ sm_mon_1_svc(struct mon *argp, struct sv + * doesn't fail. (I should probably fix this assumption.) + */ + if (!(clnt = nlist_new(my_name, mon_name, 0))) { +- note(N_WARNING, "out of memory"); ++ xlog_warn("out of memory"); + goto failure; + } + +- NL_ADDR(clnt) = my_addr; ++ NL_ADDR(clnt) = my_addr.sin_addr; + NL_MY_PROG(clnt) = id->my_prog; + NL_MY_VERS(clnt) = id->my_vers; + NL_MY_PROC(clnt) = id->my_proc; +@@ -182,40 +181,16 @@ sm_mon_1_svc(struct mon *argp, struct sv + /* + * Now, Create file on stable storage for host. + */ +- +- path=xmalloc(strlen(SM_DIR)+strlen(dnsname)+2); +- sprintf(path, "%s/%s", SM_DIR, dnsname); +- if ((fd = open(path, O_WRONLY|O_SYNC|O_CREAT|O_APPEND, +- S_IRUSR|S_IWUSR)) < 0) { +- /* Didn't fly. We won't monitor. */ +- note(N_ERROR, "creat(%s) failed: %s", path, strerror (errno)); ++ if (!nsm_insert_monitored_host(dnsname, ++ (struct sockaddr *)(char *)&my_addr, argp)) { + nlist_free(NULL, clnt); +- free(path); + goto failure; + } +- { +- char buf[LINELEN + 1 + SM_MAXSTRLEN*2 + 4]; +- char *e; +- int i; +- e = buf + sprintf(buf, "%08x %08x %08x %08x ", +- my_addr.s_addr, id->my_prog, +- id->my_vers, id->my_proc); +- for (i=0; ipriv[i])); +- if (e+1-buf != LINELEN) abort(); +- e += sprintf(e, " %s %s\n", mon_name, my_name); +- if (write(fd, buf, e-buf) != (e-buf)) { +- note(N_WARNING, "writing to %s failed: errno %d (%s)", +- path, errno, strerror(errno)); +- } +- } + +- free(path); + /* PRC: do the HA callout: */ + ha_callout("add-client", mon_name, my_name, -1); + nlist_insert(&rtnl, clnt); +- close(fd); +- dprintf(N_DEBUG, "MONITORING %s for %s", mon_name, my_name); ++ xlog(D_GENERAL, "MONITORING %s for %s", mon_name, my_name); + success: + result.res_stat = STAT_SUCC; + /* SUN's sm_inter.x says this should be "state number of local site". +@@ -232,75 +207,50 @@ sm_mon_1_svc(struct mon *argp, struct sv + return (&result); + + failure: +- note(N_WARNING, "STAT_FAIL to %s for SM_MON of %s", my_name, mon_name); ++ xlog_warn("STAT_FAIL to %s for SM_MON of %s", my_name, mon_name); + return (&result); + } + +-void load_state(void) ++static unsigned int ++load_one_host(const char *hostname, const struct sockaddr *sap, ++ const struct mon *m, ++ __attribute__ ((unused)) const time_t timestamp) + { +- DIR *d; +- struct dirent *de; +- char buf[LINELEN + 1 + SM_MAXSTRLEN + 2]; +- +- d = opendir(SM_DIR); +- if (!d) +- return; +- while ((de = readdir(d))) { +- char *path; +- FILE *f; +- int p; +- +- if (de->d_name[0] == '.') +- continue; +- path = xmalloc(strlen(SM_DIR)+strlen(de->d_name)+2); +- sprintf(path, "%s/%s", SM_DIR, de->d_name); +- f = fopen(path, "r"); +- free(path); +- if (f == NULL) +- continue; +- while (fgets(buf, sizeof(buf), f) != NULL) { +- int addr, proc, prog, vers; +- char priv[SM_PRIV_SIZE]; +- char *monname, *myname; +- char *b; +- int i; +- notify_list *clnt; +- +- buf[sizeof(buf)-1] = 0; +- b = strchr(buf, '\n'); +- if (b) *b = 0; +- sscanf(buf, "%x %x %x %x ", +- &addr, &prog, &vers, &proc); +- b = buf+36; +- for (i=0; idns_name = xstrdup(de->d_name); +- memcpy(NL_PRIV(clnt), priv, SM_PRIV_SIZE); +- nlist_insert(&rtnl, clnt); +- } +- fclose(f); ++ const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; ++ notify_list *clnt; ++ ++ clnt = nlist_new(m->mon_id.my_id.my_name, ++ m->mon_id.mon_name, 0); ++ if (clnt == NULL) ++ return 0; ++ ++ clnt->dns_name = strdup(hostname); ++ if (clnt->dns_name == NULL) { ++ nlist_free(NULL, clnt); ++ return 0; + } +- closedir(d); +-} + ++ xlog(D_GENERAL, "Adding record for %s to the monitor list...", ++ hostname); ++ ++ NL_ADDR(clnt) = sin->sin_addr; ++ NL_MY_PROG(clnt) = m->mon_id.my_id.my_prog; ++ NL_MY_VERS(clnt) = m->mon_id.my_id.my_vers; ++ NL_MY_PROC(clnt) = m->mon_id.my_id.my_proc; ++ memcpy(NL_PRIV(clnt), m->priv, SM_PRIV_SIZE); ++ ++ nlist_insert(&rtnl, clnt); ++ return 1; ++} + ++void load_state(void) ++{ ++ unsigned int count; + ++ count = nsm_load_monitor_list(load_one_host); ++ if (count) ++ xlog(D_GENERAL, "Loaded %u previously monitored hosts"); ++} + + /* + * Services SM_UNMON requests. +@@ -320,6 +270,8 @@ sm_unmon_1_svc(struct mon_id *argp, stru + struct my_id *id = &argp->my_id; + char *cp; + ++ xlog(D_CALL, "Received SM_UNMON for %s from %s", mon_name, my_name); ++ + result.state = MY_STATE; + + if (!caller_is_localhost(rqstp)) +@@ -333,9 +285,8 @@ sm_unmon_1_svc(struct mon_id *argp, stru + + /* Check if we're monitoring anyone. */ + if (rtnl == NULL) { +- note(N_WARNING, +- "Received SM_UNMON request from %s for %s while not " +- "monitoring any hosts.", my_name, argp->mon_name); ++ xlog_warn("Received SM_UNMON request from %s for %s while not " ++ "monitoring any hosts", my_name, argp->mon_name); + return (&result); + } + clnt = rtnl; +@@ -352,13 +303,13 @@ sm_unmon_1_svc(struct mon_id *argp, stru + NL_MY_PROG(clnt) == id->my_prog && + NL_MY_VERS(clnt) == id->my_vers) { + /* Match! */ +- dprintf(N_DEBUG, "UNMONITORING %s for %s", ++ xlog(D_GENERAL, "UNMONITORING %s for %s", + mon_name, my_name); + + /* PRC: do the HA callout: */ + ha_callout("del-client", mon_name, my_name, -1); + +- xunlink(SM_DIR, clnt->dns_name); ++ nsm_delete_monitored_host(clnt->dns_name); + nlist_free(&rtnl, clnt); + + return (&result); +@@ -367,7 +318,7 @@ sm_unmon_1_svc(struct mon_id *argp, stru + } + + failure: +- note(N_WARNING, "Received erroneous SM_UNMON request from %s for %s", ++ xlog_warn("Received erroneous SM_UNMON request from %s for %s", + my_name, mon_name); + return (&result); + } +@@ -381,13 +332,15 @@ sm_unmon_all_1_svc(struct my_id *argp, s + notify_list *clnt; + char *my_name = argp->my_name; + ++ xlog(D_CALL, "Received SM_UNMON_ALL for %s", my_name); ++ + if (!caller_is_localhost(rqstp)) + goto failure; + + result.state = MY_STATE; + + if (rtnl == NULL) { +- note(N_WARNING, "Received SM_UNMON_ALL request from %s " ++ xlog_warn("Received SM_UNMON_ALL request from %s " + "while not monitoring any hosts", my_name); + return (&result); + } +@@ -401,7 +354,7 @@ sm_unmon_all_1_svc(struct my_id *argp, s + char mon_name[SM_MAXSTRLEN + 1]; + notify_list *temp; + +- dprintf(N_DEBUG, ++ xlog(D_GENERAL, + "UNMONITORING (SM_UNMON_ALL) %s for %s", + NL_MON_NAME(clnt), NL_MY_NAME(clnt)); + strncpy(mon_name, NL_MON_NAME(clnt), +@@ -410,7 +363,7 @@ sm_unmon_all_1_svc(struct my_id *argp, s + temp = NL_NEXT(clnt); + /* PRC: do the HA callout: */ + ha_callout("del-client", mon_name, my_name, -1); +- xunlink(SM_DIR, clnt->dns_name); ++ nsm_delete_monitored_host(clnt->dns_name); + nlist_free(&rtnl, clnt); + ++count; + clnt = temp; +@@ -419,8 +372,8 @@ sm_unmon_all_1_svc(struct my_id *argp, s + } + + if (!count) { +- dprintf(N_DEBUG, "SM_UNMON_ALL request from %s with no " +- "SM_MON requests from it.", my_name); ++ xlog(D_GENERAL, "SM_UNMON_ALL request from %s with no " ++ "SM_MON requests from it", my_name); + } + + failure: +diff -up nfs-utils-1.2.1/utils/statd/rmtcall.c.orig nfs-utils-1.2.1/utils/statd/rmtcall.c +--- nfs-utils-1.2.1/utils/statd/rmtcall.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/statd/rmtcall.c 2010-01-12 06:07:40.784878513 -0500 +@@ -43,7 +43,6 @@ + #include "sm_inter.h" + #include "statd.h" + #include "notlist.h" +-#include "log.h" + #include "ha-callout.h" + + #if SIZEOF_SOCKLEN_T - 0 == 0 +@@ -81,7 +80,7 @@ statd_get_socket(void) + if (sockfd >= 0) close(sockfd); + + if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { +- note(N_CRIT, "%s: Can't create socket: %m", __func__); ++ xlog(L_ERROR, "%s: Can't create socket: %m", __func__); + return -1; + } + +@@ -91,7 +90,7 @@ statd_get_socket(void) + sin.sin_addr.s_addr = INADDR_ANY; + + if (bindresvport(sockfd, &sin) < 0) { +- dprintf(N_WARNING, "%s: can't bind to reserved port", ++ xlog(D_GENERAL, "%s: can't bind to reserved port", + __func__); + break; + } +@@ -150,7 +149,7 @@ xmit_call(struct sockaddr_in *sin, + + /* Encode the RPC header part and payload */ + if (!xdr_callmsg(xdrs, &mesg) || !func(xdrs, obj)) { +- dprintf(N_WARNING, "%s: can't encode RPC message!", __func__); ++ xlog(D_GENERAL, "%s: can't encode RPC message!", __func__); + xdr_destroy(xdrs); + return 0; + } +@@ -160,9 +159,9 @@ xmit_call(struct sockaddr_in *sin, + + if ((err = sendto(sockfd, msgbuf, msglen, 0, + (struct sockaddr *) sin, sizeof(*sin))) < 0) { +- dprintf(N_WARNING, "%s: sendto failed: %m", __func__); ++ xlog_warn("%s: sendto failed: %m", __func__); + } else if (err != msglen) { +- dprintf(N_WARNING, "%s: short write: %m", __func__); ++ xlog_warn("%s: short write: %m", __func__); + } + + xdr_destroy(xdrs); +@@ -182,7 +181,7 @@ recv_rply(struct sockaddr_in *sin, u_lon + /* Receive message */ + if ((msglen = recvfrom(sockfd, msgbuf, sizeof(msgbuf), 0, + (struct sockaddr *) sin, &alen)) < 0) { +- dprintf(N_WARNING, "%s: recvfrom failed: %m", __func__); ++ xlog_warn("%s: recvfrom failed: %m", __func__); + return NULL; + } + +@@ -194,19 +193,19 @@ recv_rply(struct sockaddr_in *sin, u_lon + mesg.rm_reply.rp_acpt.ar_results.proc = (xdrproc_t) xdr_void; + + if (!xdr_replymsg(xdrs, &mesg)) { +- note(N_WARNING, "%s: can't decode RPC message!", __func__); ++ xlog_warn("%s: can't decode RPC message!", __func__); + goto done; + } + + if (mesg.rm_reply.rp_stat != 0) { +- note(N_WARNING, "%s: [%s] RPC status %d", ++ xlog_warn("%s: [%s] RPC status %d", + __func__, + inet_ntoa(sin->sin_addr), + mesg.rm_reply.rp_stat); + goto done; + } + if (mesg.rm_reply.rp_acpt.ar_stat != 0) { +- note(N_WARNING, "%s: [%s] RPC status %d", ++ xlog_warn("%s: [%s] RPC status %d", + __func__, + inet_ntoa(sin->sin_addr), + mesg.rm_reply.rp_acpt.ar_stat); +@@ -224,14 +223,13 @@ recv_rply(struct sockaddr_in *sin, u_lon + strncpy (addr, inet_ntoa(lp->addr), + sizeof (addr) - 1); + addr [sizeof (addr) - 1] = '\0'; +- dprintf(N_WARNING, "%s: address mismatch: " ++ xlog_warn("%s: address mismatch: " + "expected %s, got %s", __func__, + addr, inet_ntoa(sin->sin_addr)); + } + if (lp->port == 0) { + if (!xdr_u_long(xdrs, portp)) { +- note(N_WARNING, +- "%s: [%s] can't decode reply body!", ++ xlog_warn("%s: [%s] can't decode reply body!", + __func__, + inet_ntoa(sin->sin_addr)); + lp = NULL; +@@ -260,7 +258,7 @@ process_entry(notify_list *lp) + /* __u32 proc, vers, prog; */ + + if (NL_TIMES(lp) == 0) { +- note(N_DEBUG, "%s: Cannot notify %s, giving up.", ++ xlog(D_GENERAL, "%s: Cannot notify %s, giving up", + __func__, inet_ntoa(NL_ADDR(lp))); + return 0; + } +@@ -286,7 +284,7 @@ process_entry(notify_list *lp) + + lp->xid = xmit_call(&sin, prog, vers, proc, func, objp); + if (!lp->xid) { +- note(N_WARNING, "%s: failed to notify port %d", ++ xlog_warn("%s: failed to notify port %d", + __func__, ntohs(lp->port)); + } + NL_TIMES(lp) -= 1; +@@ -319,10 +317,10 @@ process_reply(FD_SET_TYPE *rfds) + nlist_insert_timer(¬ify, lp); + return 1; + } +- note(N_WARNING, "%s: [%s] service %d not registered", ++ xlog_warn("%s: [%s] service %d not registered", + __func__, inet_ntoa(lp->addr), NL_MY_PROG(lp)); + } else { +- dprintf(N_DEBUG, "%s: Callback to %s (for %d) succeeded.", ++ xlog(D_GENERAL, "%s: Callback to %s (for %d) succeeded", + __func__, NL_MY_NAME(lp), NL_MON_NAME(lp)); + } + nlist_free(¬ify, lp); +@@ -346,8 +344,8 @@ process_notify_list(void) + nlist_remove(¬ify, entry); + nlist_insert_timer(¬ify, entry); + } else { +- note(N_ERROR, +- "%s: Can't callback %s (%d,%d), giving up.", ++ xlog(L_ERROR, ++ "%s: Can't callback %s (%d,%d), giving up", + __func__, + NL_MY_NAME(entry), + NL_MY_PROG(entry), +diff -up nfs-utils-1.2.1/utils/statd/simu.c.orig nfs-utils-1.2.1/utils/statd/simu.c +--- nfs-utils-1.2.1/utils/statd/simu.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/statd/simu.c 2010-01-12 06:07:40.785889142 -0500 +@@ -27,24 +27,26 @@ sm_simu_crash_1_svc (void *argp, struct + static char *result = NULL; + struct in_addr caller; + ++ xlog(D_CALL, "Received SM_SIMU_CRASH"); ++ + if (sin->sin_family != AF_INET) { +- note(N_WARNING, "Call to statd from non-AF_INET address"); ++ xlog_warn("Call to statd from non-AF_INET address"); + goto failure; + } + + caller = sin->sin_addr; + if (caller.s_addr != htonl(INADDR_LOOPBACK)) { +- note(N_WARNING, "Call to statd from non-local host %s", ++ xlog_warn("Call to statd from non-local host %s", + inet_ntoa(caller)); + goto failure; + } + + if (ntohs(sin->sin_port) >= 1024) { +- note(N_WARNING, "Call to statd-simu-crash from unprivileged port"); ++ xlog_warn("Call to statd-simu-crash from unprivileged port"); + goto failure; + } + +- note (N_WARNING, "*** SIMULATING CRASH! ***"); ++ xlog_warn("*** SIMULATING CRASH! ***"); + my_svc_exit (); + + if (rtnl) +diff -up nfs-utils-1.2.1/utils/statd/simulate.c.orig nfs-utils-1.2.1/utils/statd/simulate.c +--- nfs-utils-1.2.1/utils/statd/simulate.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/statd/simulate.c 2010-01-12 06:07:40.786878392 -0500 +@@ -38,7 +38,9 @@ extern void svc_exit (void); + void + simulator (int argc, char **argv) + { +- log_enable (1); ++ xlog_stderr (1); ++ xlog_syslog (0); ++ xlog_open ("statd simulator"); + + if (argc == 2) + if (!strcasecmp (*argv, "crash")) +@@ -61,7 +63,7 @@ simulator (int argc, char **argv) + simulate_mon (*(&argv[1]), *(&argv[2]), *(&argv[3]), *(&argv[4]), + *(&argv[5])); + } +- die ("WTF? Give me something I can use!"); ++ xlog_err ("WTF? Give me something I can use!"); + } + + static void +@@ -72,11 +74,11 @@ simulate_mon (char *calling, char *monit + sm_stat_res *result; + mon mon; + +- dprintf (N_DEBUG, "Calling %s (as %s) to monitor %s", calling, as, ++ xlog (D_GENERAL, "Calling %s (as %s) to monitor %s", calling, as, + monitoring); + + if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL) +- die ("%s", clnt_spcreateerror ("clnt_create")); ++ xlog_err ("%s", clnt_spcreateerror ("clnt_create")); + + memcpy (mon.priv, fool, SM_PRIV_SIZE); + mon.mon_id.my_id.my_name = xstrdup (as); +@@ -87,16 +89,15 @@ simulate_mon (char *calling, char *monit + mon.mon_id.mon_name = monitoring; + + if (!(result = sm_mon_1 (&mon, client))) +- die ("%s", clnt_sperror (client, "sm_mon_1")); ++ xlog_err ("%s", clnt_sperror (client, "sm_mon_1")); + + free (mon.mon_id.my_id.my_name); + + if (result->res_stat != STAT_SUCC) { +- note (N_FATAL, "SM_MON request failed, state: %d", result->state); +- exit (0); ++ xlog_err ("SM_MON request failed, state: %d", result->state); + } else { +- dprintf (N_DEBUG, "SM_MON result successful, state: %d\n", result->state); +- dprintf (N_DEBUG, "Waiting for callback."); ++ xlog (D_GENERAL, "SM_MON result successful, state: %d\n", result->state); ++ xlog (D_GENERAL, "Waiting for callback"); + daemon_simulator (); + exit (0); + } +@@ -109,11 +110,11 @@ simulate_unmon (char *calling, char *unm + sm_stat *result; + mon_id mon_id; + +- dprintf (N_DEBUG, "Calling %s (as %s) to unmonitor %s", calling, as, ++ xlog (D_GENERAL, "Calling %s (as %s) to unmonitor %s", calling, as, + unmonitoring); + + if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL) +- die ("%s", clnt_spcreateerror ("clnt_create")); ++ xlog_err ("%s", clnt_spcreateerror ("clnt_create")); + + mon_id.my_id.my_name = xstrdup (as); + mon_id.my_id.my_prog = atoi (proggy) * SIM_SM_PROG; +@@ -122,10 +123,10 @@ simulate_unmon (char *calling, char *unm + mon_id.mon_name = unmonitoring; + + if (!(result = sm_unmon_1 (&mon_id, client))) +- die ("%s", clnt_sperror (client, "sm_unmon_1")); ++ xlog_err ("%s", clnt_sperror (client, "sm_unmon_1")); + + free (mon_id.my_id.my_name); +- dprintf (N_DEBUG, "SM_UNMON request returned state: %d\n", result->state); ++ xlog (D_GENERAL, "SM_UNMON request returned state: %d\n", result->state); + exit (0); + } + +@@ -136,10 +137,10 @@ simulate_unmon_all (char *calling, char + sm_stat *result; + my_id my_id; + +- dprintf (N_DEBUG, "Calling %s (as %s) to unmonitor all hosts", calling, as); ++ xlog (D_GENERAL, "Calling %s (as %s) to unmonitor all hosts", calling, as); + + if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL) +- die ("%s", clnt_spcreateerror ("clnt_create")); ++ xlog_err ("%s", clnt_spcreateerror ("clnt_create")); + + my_id.my_name = xstrdup (as); + my_id.my_prog = atoi (proggy) * SIM_SM_PROG; +@@ -147,10 +148,10 @@ simulate_unmon_all (char *calling, char + my_id.my_proc = SIM_SM_MON; + + if (!(result = sm_unmon_all_1 (&my_id, client))) +- die ("%s", clnt_sperror (client, "sm_unmon_all_1")); ++ xlog_err ("%s", clnt_sperror (client, "sm_unmon_all_1")); + + free (my_id.my_name); +- dprintf (N_DEBUG, "SM_UNMON_ALL request returned state: %d\n", result->state); ++ xlog (D_GENERAL, "SM_UNMON_ALL request returned state: %d\n", result->state); + exit (0); + } + +@@ -160,10 +161,10 @@ simulate_crash (char *host) + CLIENT *client; + + if ((client = clnt_create (host, SM_PROG, SM_VERS, "udp")) == NULL) +- die ("%s", clnt_spcreateerror ("clnt_create")); ++ xlog_err ("%s", clnt_spcreateerror ("clnt_create")); + + if (!sm_simu_crash_1 (NULL, client)) +- die ("%s", clnt_sperror (client, "sm_simu_crash_1")); ++ xlog_err ("%s", clnt_sperror (client, "sm_simu_crash_1")); + + exit (0); + } +@@ -176,18 +177,18 @@ simulate_stat (char *calling, char *moni + sm_stat_res *result; + + if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL) +- die ("%s", clnt_spcreateerror ("clnt_create")); ++ xlog_err ("%s", clnt_spcreateerror ("clnt_create")); + + checking.mon_name = monitoring; + + if (!(result = sm_stat_1 (&checking, client))) +- die ("%s", clnt_sperror (client, "sm_stat_1")); ++ xlog_err ("%s", clnt_sperror (client, "sm_stat_1")); + + if (result->res_stat == STAT_SUCC) +- dprintf (N_DEBUG, "STAT_SUCC from %s for %s, state: %d", calling, ++ xlog (D_GENERAL, "STAT_SUCC from %s for %s, state: %d", calling, + monitoring, result->state); + else +- dprintf (N_DEBUG, "STAT_FAIL from %s for %s, state: %d", calling, ++ xlog (D_GENERAL, "STAT_FAIL from %s for %s, state: %d", calling, + monitoring, result->state); + + exit (0); +@@ -196,9 +197,8 @@ simulate_stat (char *calling, char *moni + static void + sim_killer (int sig) + { +- note (N_FATAL, "Simulator caught signal %d, un-registering and exiting.", sig); + pmap_unset (sim_port, SIM_SM_VERS); +- exit (0); ++ xlog_err ("Simulator caught signal %d, un-registering and exiting", sig); + } + + static void +@@ -219,7 +219,7 @@ sim_sm_mon_1_svc (struct status *argp, s + { + static char *result; + +- dprintf (N_DEBUG, "Recieved state %d for mon_name %s (opaque \"%s\")", ++ xlog (D_GENERAL, "Recieved state %d for mon_name %s (opaque \"%s\")", + argp->state, argp->mon_name, argp->priv); + svc_exit (); + return ((void *)&result); +diff -up nfs-utils-1.2.1/utils/statd/sm-notify.c.orig nfs-utils-1.2.1/utils/statd/sm-notify.c +--- nfs-utils-1.2.1/utils/statd/sm-notify.c.orig 2010-01-12 06:06:44.313836268 -0500 ++++ nfs-utils-1.2.1/utils/statd/sm-notify.c 2010-01-12 06:09:23.901815847 -0500 +@@ -8,6 +8,7 @@ + #include + #endif + ++#include + #include + #include + #include +@@ -28,24 +29,9 @@ + #include + #include + +-#define STATD_PATH_XTN "statd/" +- +-#ifndef BASEDIR +-# ifdef NFS_STATEDIR +-# define BASEDIR NFS_STATEDIR "/" STATD_PATH_XTN +-# else +-# define BASEDIR "/var/lib/nfs" "/" STATD_PATH_XTN +-# 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 "xlog.h" ++#include "nsm.h" ++#include "nfsrpc.h" + + #define NSM_PROG 100024 + #define NSM_PROGRAM 100024 +@@ -58,7 +44,6 @@ char *_SM_BAK_PATH = DEFAULT_SM_BAK_PATH + struct nsm_host { + struct nsm_host * next; + char * name; +- char * path; + struct sockaddr_storage addr; + struct addrinfo *ai; + time_t last_used; +@@ -69,57 +54,22 @@ struct nsm_host { + }; + + static char nsm_hostname[256]; +-static uint32_t nsm_state; ++static int nsm_state; + 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 unsigned int nsm_get_state(int); + static void notify(void); + 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 +- */ +- +-static unsigned short smn_get_port(const struct sockaddr *sap) +-{ +- 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); +- } +- return 0; +-} +- +-static void smn_set_port(struct sockaddr *sap, const unsigned short port) +-{ +- 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; +- } +-} +- + static struct addrinfo *smn_lookup(const char *name) + { + struct addrinfo *ai, hint = { +@@ -132,27 +82,47 @@ static struct addrinfo *smn_lookup(const + int error; + + error = getaddrinfo(name, NULL, &hint, &ai); +- switch (error) { +- case 0: +- return ai; +- case EAI_SYSTEM: +- if (opt_debug) +- nsm_log(LOG_ERR, "getaddrinfo(3): %s", +- strerror(errno)); +- break; +- default: +- if (opt_debug) +- nsm_log(LOG_ERR, "getaddrinfo(3): %s", +- gai_strerror(error)); ++ if (error) { ++ 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 time_t timestamp) ++{ ++ struct nsm_host *host; ++ ++ host = calloc(1, sizeof(*host)); ++ if (host == NULL) ++ goto out_nomem; ++ ++ host->name = strdup(hostname); ++ if (host->name == NULL) { ++ 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) + { +- unlink(host->path); +- free(host->path); ++ xlog(D_CALL, "Removing %s from notify list", host->name); ++ ++ nsm_delete_notified_host(host->name); ++ + free(host->name); + if (host->ai) + freeaddrinfo(host->ai); +@@ -160,13 +130,37 @@ static void smn_forget_host(struct nsm_h + free(host); + } + ++static unsigned int ++smn_get_host(const char *hostname, ++ __attribute__ ((unused)) const struct sockaddr *sap, ++ __attribute__ ((unused)) const struct mon *m, ++ const time_t timestamp) ++{ ++ struct nsm_host *host; ++ ++ host = smn_alloc_host(hostname, timestamp); ++ if (host == NULL) ++ return 0; ++ ++ insert_host(host); ++ xlog(D_GENERAL, "Added host %s to notify list", hostname); ++ return 1; ++} ++ + int + main(int argc, char **argv) + { + int c; + int 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; +@@ -178,7 +172,7 @@ 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); +@@ -186,24 +180,9 @@ main(int argc, char **argv) + 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: +@@ -213,18 +192,26 @@ 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); + } + } +@@ -233,31 +220,26 @@ usage: fprintf(stderr, + 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)); ++ xlog(L_ERROR, "Failed to obtain name of local host: %m"); + exit(1); + } + +- 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); + } + +@@ -273,8 +255,7 @@ usage: fprintf(stderr, + + 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); +@@ -298,8 +279,7 @@ notify(void) + retry: + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { +- nsm_log(LOG_ERR, "Failed to create RPC socket: %s", +- strerror(errno)); ++ xlog(L_ERROR, "Failed to create RPC socket: %m"); + exit(1); + } + fcntl(sock, F_SETFL, O_NONBLOCK); +@@ -311,7 +291,7 @@ notify(void) + if (opt_srcaddr) { + struct addrinfo *ai = smn_lookup(opt_srcaddr); + if (!ai) { +- nsm_log(LOG_ERR, ++ xlog(L_ERROR, + "Not a valid hostname or address: \"%s\"", + opt_srcaddr); + exit(1); +@@ -326,10 +306,9 @@ notify(void) + /* Use source port if provided on the command line, + * otherwise use bindresvport */ + if (opt_srcport) { +- smn_set_port(local_addr, opt_srcport); ++ nfs_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)); ++ xlog(L_ERROR, "Failed to bind RPC socket: %m"); + exit(1); + } + } else { +@@ -348,7 +327,8 @@ notify(void) + if (opt_max_retry) + failtime = time(NULL) + opt_max_retry; + +- drop_privs(); ++ if (!nsm_drop_privileges(-1)) ++ exit(1); + + while (hosts) { + struct pollfd pfd; +@@ -385,7 +365,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; +@@ -422,8 +402,7 @@ notify_host(int sock, struct nsm_host *h + if (host->ai == NULL) { + host->ai = smn_lookup(host->name); + if (host->ai == NULL) { +- nsm_log(LOG_WARNING, +- "DNS resolution of %s failed; " ++ xlog_warn("DNS resolution of %s failed; " + "retrying later", host->name); + return 0; + } +@@ -462,16 +441,16 @@ notify_host(int sock, struct nsm_host *h + first->ai_addrlen); + } + +- smn_set_port((struct sockaddr *)&host->addr, 0); ++ nfs_set_port((struct sockaddr *)&host->addr, 0); + host->retries = 0; + } + + memcpy(dest, &host->addr, destlen); +- if (smn_get_port(dest) == 0) { ++ if (nfs_get_port(dest) == 0) { + /* Build a PMAP packet */ +- nsm_log(LOG_DEBUG, "Sending portmap query to %s", host->name); ++ xlog(D_GENERAL, "Sending portmap query to %s", host->name); + +- smn_set_port(dest, 111); ++ nfs_set_port(dest, 111); + *p++ = htonl(100000); + *p++ = htonl(2); + *p++ = htonl(3); +@@ -486,7 +465,7 @@ notify_host(int sock, struct nsm_host *h + *p++ = 0; + } else { + /* Build an SM_NOTIFY packet */ +- nsm_log(LOG_DEBUG, "Sending SM_NOTIFY to %s", host->name); ++ xlog(D_GENERAL, "Sending SM_NOTIFY to %s", host->name); + + *p++ = htonl(NSM_PROGRAM); + *p++ = htonl(NSM_VERSION); +@@ -506,8 +485,8 @@ notify_host(int sock, struct nsm_host *h + len = (p - msgbuf) << 2; + + 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)); ++ xlog_warn("Sending Reboot Notification to " ++ "'%s' failed: errno %d (%m)", host->name, errno); + + return 0; + } +@@ -528,7 +507,7 @@ recv_reply(int sock) + if (res < 0) + return; + +- nsm_log(LOG_DEBUG, "Received packet..."); ++ xlog(D_GENERAL, "Received packet..."); + + p = msgbuf; + end = p + (res >> 2); +@@ -547,7 +526,7 @@ recv_reply(int sock) + return; + sap = (struct sockaddr *)&hp->addr; + +- if (smn_get_port(sap) == 0) { ++ if (nfs_get_port(sap) == 0) { + /* This was a portmap request */ + unsigned int port; + +@@ -559,11 +538,11 @@ recv_reply(int sock) + 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); ++ xlog(D_GENERAL, "No statd on %s", hp->name); + hp->timeout = NSM_MAX_TIMEOUT; + hp->send_next += NSM_MAX_TIMEOUT; + } else { +- smn_set_port(sap, port); ++ nfs_set_port(sap, port); + if (hp->timeout >= NSM_MAX_TIMEOUT / 4) + hp->timeout = NSM_MAX_TIMEOUT / 4; + } +@@ -575,7 +554,7 @@ recv_reply(int sock) + * packet) + */ + if (p <= end) { +- nsm_log(LOG_DEBUG, "Host %s notified successfully", ++ xlog(D_GENERAL, "Host %s notified successfully", + hp->name); + smn_forget_host(hp); + return; +@@ -587,87 +566,6 @@ fail: /* Re-insert the host */ + } + + /* +- * Back up all hosts from the sm directory to sm.bak +- */ +-static void +-backup_hosts(const char *dirname, const char *bakname) +-{ +- 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); +- } +- } +- closedir(dir); +-} +- +-/* +- * Get all entries from sm.bak and convert them to host entries +- */ +-static void +-get_hosts(const char *dirname) +-{ +- 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; +- } +- +- host = NULL; +- while ((de = readdir(dir)) != NULL) { +- struct stat stb; +- char path[1024]; +- +- 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; +- } +- +- snprintf(path, sizeof(path), "%s/%s", dirname, de->d_name); +- if (stat(path, &stb) < 0) +- 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 */ +- +- insert_host(host); +- host = NULL; +- } +- closedir(dir); +- +- if (host) +- free(host); +-} +- +-/* + * Insert host into sorted list + */ + static void +@@ -714,84 +612,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 +@@ -801,61 +621,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; +- if (write(fd, pid, strlen(pid)) != strlen(pid)) { +- nsm_log(LOG_WARNING, "Writing to pid file failed: errno %d(%s)", +- errno, strerror(errno)); +- } +- 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; +- const char *file = "/proc/sys/fs/nfs/nsm_local_state"; + +- fd = open(file ,O_WRONLY); +- if (fd >= 0) { +- char buf[20]; +- snprintf(buf, sizeof(buf), "%d", state); +- if (write(fd, buf, strlen(buf)) != strlen(buf)) { +- nsm_log(LOG_WARNING, "Writing to '%s' failed: errno %d (%s)", +- file, errno, strerror(errno)); +- } +- close(fd); +- } ++ (void)close(fd); ++ return 1; + } +diff -up nfs-utils-1.2.1/utils/statd/sm-notify.man.orig nfs-utils-1.2.1/utils/statd/sm-notify.man +--- nfs-utils-1.2.1/utils/statd/sm-notify.man.orig 2010-01-12 06:06:44.313836268 -0500 ++++ nfs-utils-1.2.1/utils/statd/sm-notify.man 2010-01-12 06:07:40.788880334 -0500 +@@ -6,7 +6,7 @@ + .SH NAME + sm-notify \- Send out NSM reboot notifications + .SH SYNOPSIS +-.BI "/sbin/sm-notify [-dfq] [-m " time "] [-p " port "] [-P " path "] [-v " my_name " ] ++.BI "/sbin/sm-notify [-df] [-m " time "] [-p " port "] [-P " path "] [-v " my_name " ] + .SH DESCRIPTION + File locking over NFS (v2 and v3) requires a facility to notify peers in + case of a reboot, so that clients can reclaim locks after +@@ -101,10 +101,6 @@ to bind to the indicated IP + number. If this option is not given, it will try to bind to + a randomly chosen privileged port below 1024. + .TP +-.B -q +-Be quiet. This suppresses all messages except error +-messages while collecting the list of hosts. +-.TP + .BI -P " /path/to/state/directory + If + .B sm-notify +diff -up nfs-utils-1.2.1/utils/statd/stat.c.orig nfs-utils-1.2.1/utils/statd/stat.c +--- nfs-utils-1.2.1/utils/statd/stat.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/statd/stat.c 2010-01-12 06:07:40.789888702 -0500 +@@ -42,13 +42,15 @@ sm_stat_1_svc (struct sm_name *argp, str + { + static sm_stat_res result; + ++ xlog(D_CALL, "Received SM_STAT from %s", argp->mon_name); ++ + if (gethostbyname (argp->mon_name) == NULL) { +- note (N_WARNING, "gethostbyname error for %s", argp->mon_name); ++ xlog_warn ("gethostbyname error for %s", argp->mon_name); + result.res_stat = STAT_FAIL; +- dprintf (N_DEBUG, "STAT_FAIL for %s", argp->mon_name); ++ xlog (D_GENERAL, "STAT_FAIL for %s", argp->mon_name); + } else { + result.res_stat = STAT_SUCC; +- dprintf (N_DEBUG, "STAT_SUCC for %s", argp->mon_name); ++ xlog (D_GENERAL, "STAT_SUCC for %s", argp->mon_name); + } + result.state = MY_STATE; + return(&result); +diff -up nfs-utils-1.2.1/utils/statd/statd.c.orig nfs-utils-1.2.1/utils/statd/statd.c +--- nfs-utils-1.2.1/utils/statd/statd.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/statd/statd.c 2010-01-12 06:07:40.789888702 -0500 +@@ -25,33 +25,21 @@ + #include + #include + #include ++ + #include "statd.h" +-#include "version.h" + #include "nfslib.h" ++#include "nsm.h" + + /* Socket operations */ + #include + #include + +-/* Added to enable specification of state directory path at run-time +- * j_carlos_gomez@yahoo.com +- */ +- +-char * DIR_BASE = DEFAULT_DIR_BASE; +- +-char * SM_DIR = DEFAULT_SM_DIR; +-char * SM_BAK_DIR = DEFAULT_SM_BAK_DIR; +-char * SM_STAT_PATH = DEFAULT_SM_STAT_PATH; +- +-/* ----- end of state directory path stuff ------- */ +- + int run_mode = 0; /* foreground logging mode */ + + /* LH - I had these local to main, but it seemed silly to have + * two copies of each - one in main(), one static in log.c... + * It also eliminates the 256-char static in log.c */ +-char *name_p = NULL; +-const char *version_p = NULL; ++static char *name_p = NULL; + + /* PRC: a high-availability callout program can be specified with -H + * When this is done, the program will receive callouts whenever clients +@@ -75,7 +63,6 @@ static struct option longopts[] = + }; + + extern void sm_prog_1 (struct svc_req *, register SVCXPRT *); +-static void load_state_number(void); + + #ifdef SIMULATIONS + extern void simulator (int, char **); +@@ -109,17 +96,15 @@ sm_prog_1_wrapper (struct svc_req *rqstp + static void + killer (int sig) + { +- note (N_FATAL, "Caught signal %d, un-registering and exiting.", sig); + pmap_unset (SM_PROG, SM_VERS); +- +- exit (0); ++ xlog_err ("Caught signal %d, un-registering and exiting", sig); + } + + static void + sigusr (int sig) + { + extern void my_svc_exit (void); +- dprintf (N_DEBUG, "Caught signal %d, re-notifying (state %d).", sig, ++ xlog(D_GENERAL, "Caught signal %d, re-notifying (state %d)", sig, + MY_STATE); + my_svc_exit(); + } +@@ -141,7 +126,7 @@ static void log_modes(void) + if (run_mode & MODE_LOG_STDERR) + strcat(buf,"Log-STDERR "); + +- note(N_WARNING,buf); ++ xlog_warn(buf); + } + + /* +@@ -175,13 +160,12 @@ static void create_pidfile(void) + unlink(pidfile); + fp = fopen(pidfile, "w"); + if (!fp) +- die("Opening %s failed: %s\n", +- pidfile, strerror(errno)); ++ xlog_err("Opening %s failed: %m\n", pidfile); + fprintf(fp, "%d\n", getpid()); + pidfd = dup(fileno(fp)); + if (fclose(fp) < 0) { +- note(N_WARNING, "Flushing pid file failed: errno %d (%s)\n", +- errno, strerror(errno)); ++ xlog_warn("Flushing pid file failed: errno %d (%m)\n", ++ errno); + } + } + +@@ -189,42 +173,10 @@ static void truncate_pidfile(void) + { + if (pidfd >= 0) { + if (ftruncate(pidfd, 0) < 0) { +- note(N_WARNING, "truncating pid file failed: errno %d (%s)\n", +- errno, strerror(errno)); +- } +- } +-} +- +-static void drop_privs(void) +-{ +- struct stat st; +- +- if (stat(SM_DIR, &st) == -1 && +- stat(DIR_BASE, &st) == -1) { +- st.st_uid = 0; +- st.st_gid = 0; +- } +- +- if (st.st_uid == 0) { +- note(N_WARNING, "statd running as root. chown %s to choose different user\n", +- SM_DIR); +- return; +- } +- /* better chown the pid file before dropping, as if it +- * if over nfs we might loose access +- */ +- if (pidfd >= 0) { +- if (fchown(pidfd, st.st_uid, st.st_gid) < 0) { +- note(N_ERROR, "Unable to change owner of %s: %d (%s)", +- SM_DIR, strerror (errno)); ++ xlog_warn("truncating pid file failed: errno %d (%m)\n", ++ errno); + } + } +- setgroups(0, NULL); +- if (setgid(st.st_gid) == -1 +- || setuid(st.st_uid) == -1) { +- note(N_ERROR, "Fail to drop privileges"); +- exit(1); +- } + } + + static void run_sm_notify(int outport) +@@ -266,6 +218,8 @@ int main (int argc, char **argv) + + /* Default: daemon mode, no other options */ + run_mode = 0; ++ xlog_stderr(0); ++ xlog_syslog(1); + + /* Set the basename */ + if ((name_p = strrchr(argv[0],'/')) != NULL) { +@@ -274,13 +228,6 @@ int main (int argc, char **argv) + name_p = argv[0]; + } + +- /* Get the version */ +- if ((version_p = strrchr(VERSION,' ')) != NULL) { +- version_p++; +- } else { +- version_p = VERSION; +- } +- + /* Set hostname */ + MY_NAME = NULL; + +@@ -289,7 +236,7 @@ int main (int argc, char **argv) + switch (arg) { + case 'V': /* Version */ + case 'v': +- printf("%s version %s\n",name_p,version_p); ++ printf("%s version " VERSION "\n",name_p); + exit(0); + case 'F': /* Foreground/nodaemon mode */ + run_mode |= MODE_NODAEMON; +@@ -326,34 +273,8 @@ int main (int argc, char **argv) + MY_NAME = xstrdup(optarg); + break; + case 'P': +- +- if ((DIR_BASE = xstrdup(optarg)) == NULL) { +- fprintf(stderr, "%s: xstrdup(%s) failed!\n", +- argv[0], optarg); ++ if (!nsm_setup_pathnames(argv[0], optarg)) + exit(1); +- } +- +- SM_DIR = xmalloc(strlen(DIR_BASE) + 1 + sizeof("sm")); +- SM_BAK_DIR = xmalloc(strlen(DIR_BASE) + 1 + sizeof("sm.bak")); +- SM_STAT_PATH = xmalloc(strlen(DIR_BASE) + 1 + sizeof("state")); +- +- if ((SM_DIR == NULL) +- || (SM_BAK_DIR == NULL) +- || (SM_STAT_PATH == NULL)) { +- +- fprintf(stderr, "%s: xmalloc() failed!\n", +- argv[0]); +- exit(1); +- } +- if (DIR_BASE[strlen(DIR_BASE)-1] == '/') { +- sprintf(SM_DIR, "%ssm", DIR_BASE ); +- sprintf(SM_BAK_DIR, "%ssm.bak", DIR_BASE ); +- sprintf(SM_STAT_PATH, "%sstate", DIR_BASE ); +- } else { +- sprintf(SM_DIR, "%s/sm", DIR_BASE ); +- sprintf(SM_BAK_DIR, "%s/sm.bak", DIR_BASE ); +- sprintf(SM_STAT_PATH, "%s/state", DIR_BASE ); +- } + break; + case 'H': /* PRC: specify the ha-callout program */ + if ((ha_callout_prog = xstrdup(optarg)) == NULL) { +@@ -383,7 +304,6 @@ int main (int argc, char **argv) + run_sm_notify(out_port); + } + +- + if (!(run_mode & MODE_NODAEMON)) { + run_mode &= ~MODE_LOG_STDERR; /* Never log to console in + daemon mode. */ +@@ -432,10 +352,6 @@ int main (int argc, char **argv) + /* Child. */ + close(pipefds[0]); + setsid (); +- if (chdir (DIR_BASE) == -1) { +- perror("statd: Could not chdir"); +- exit(1); +- } + + while (pipefds[1] <= 2) { + pipefds[1] = dup(pipefds[1]); +@@ -455,7 +371,13 @@ int main (int argc, char **argv) + + /* Child. */ + +- log_init (/*name_p,version_p*/); ++ if (run_mode & MODE_LOG_STDERR) { ++ xlog_syslog(0); ++ xlog_stderr(1); ++ xlog_config(D_ALL, 1); ++ } ++ xlog_open(name_p); ++ xlog(L_NOTICE, "Version " VERSION " starting"); + + log_modes(); + +@@ -495,7 +417,13 @@ int main (int argc, char **argv) + * pass on any SM_NOTIFY that arrives + */ + load_state(); +- load_state_number(); ++ ++ MY_STATE = nsm_get_state(0); ++ if (MY_STATE == 0) ++ exit(1); ++ xlog(D_GENERAL, "Local NSM state number: %d", MY_STATE); ++ nsm_update_kernel_state(MY_STATE); ++ + pmap_unset (SM_PROG, SM_VERS); + + /* this registers both UDP and TCP services */ +@@ -505,14 +433,15 @@ int main (int argc, char **argv) + if (pipefds[1] > 0) { + status = 0; + if (write(pipefds[1], &status, 1) != 1) { +- note(N_WARNING, "writing to parent pipe failed: errno %d (%s)\n", ++ xlog_warn("writing to parent pipe failed: errno %d (%s)\n", + errno, strerror(errno)); + } + close(pipefds[1]); + pipefds[1] = -1; + } + +- drop_privs(); ++ if (!nsm_drop_privileges(pidfd)) ++ exit(1); + + for (;;) { + /* +@@ -541,29 +470,3 @@ int main (int argc, char **argv) + } + return 0; + } +- +-static void +-load_state_number(void) +-{ +- int fd; +- const char *file = "/proc/sys/fs/nfs/nsm_local_state"; +- +- if ((fd = open(SM_STAT_PATH, O_RDONLY)) == -1) +- return; +- +- if (read(fd, &MY_STATE, sizeof(MY_STATE)) != sizeof(MY_STATE)) { +- note(N_WARNING, "Unable to read state from '%s': errno %d (%s)", +- SM_STAT_PATH, errno, strerror(errno)); +- } +- close(fd); +- fd = open(file, O_WRONLY); +- if (fd >= 0) { +- char buf[20]; +- snprintf(buf, sizeof(buf), "%d", MY_STATE); +- if (write(fd, buf, strlen(buf)) != strlen(buf)) +- note(N_WARNING, "Writing to '%s' failed: errno %d (%s)", +- file, errno, strerror(errno)); +- close(fd); +- } +- +-} +diff -up nfs-utils-1.2.1/utils/statd/statd.h.orig nfs-utils-1.2.1/utils/statd/statd.h +--- nfs-utils-1.2.1/utils/statd/statd.h.orig 2010-01-12 06:06:44.309847068 -0500 ++++ nfs-utils-1.2.1/utils/statd/statd.h 2010-01-12 06:10:04.934826422 -0500 +@@ -11,30 +11,7 @@ + + #include "sm_inter.h" + #include "system.h" +-#include "log.h" +- +-/* +- * Paths and filenames. +- */ +-#define STATD_PATH_XTN "statd/" +-#if defined(NFS_STATEDIR) +-# define DEFAULT_DIR_BASE NFS_STATEDIR "/" STATD_PATH_XTN +-#else +-# define DEFAULT_DIR_BASE "/var/lib/nfs/" STATD_PATH_XTN +-#endif +- +-#define DEFAULT_SM_DIR DEFAULT_DIR_BASE "sm" +-#define DEFAULT_SM_BAK_DIR DEFAULT_DIR_BASE "sm.bak" +-#define DEFAULT_SM_STAT_PATH DEFAULT_DIR_BASE "state" +- +-/* Added to support run-time specification of state directory path. +- * j_carlos_gomez@yahoo.com +- */ +- +-extern char * DIR_BASE; +-extern char * SM_DIR; +-extern char * SM_BAK_DIR; +-extern char * SM_STAT_PATH; ++#include "xlog.h" + + /* + * Status definitions. +@@ -54,7 +31,6 @@ extern int process_notify_list(void); + extern int process_reply(FD_SET_TYPE *); + extern char * xstrdup(const char *); + extern void * xmalloc(size_t); +-extern void xunlink (char *, char *); + extern void load_state(void); + + /* +@@ -85,10 +61,3 @@ extern int run_mode; + * another host.... */ + #define STATIC_HOSTNAME 8 /* Always use the hostname set by -n */ + #define MODE_NO_NOTIFY 16 /* Don't notify peers of a reboot */ +-/* +- * Program name and version pointers -- See statd.c for the reasoning +- * as to why they're global. +- */ +-extern char *name_p; /* program basename */ +-extern const char *version_p; /* program version */ +- +diff -up nfs-utils-1.2.1/utils/statd/svc_run.c.orig nfs-utils-1.2.1/utils/statd/svc_run.c +--- nfs-utils-1.2.1/utils/statd/svc_run.c.orig 2009-11-04 06:13:56.000000000 -0500 ++++ nfs-utils-1.2.1/utils/statd/svc_run.c 2010-01-12 06:07:40.791878936 -0500 +@@ -101,12 +101,12 @@ my_svc_run(void) + + tv.tv_sec = NL_WHEN(notify) - now; + tv.tv_usec = 0; +- dprintf(N_DEBUG, "Waiting for reply... (timeo %d)", ++ xlog(D_GENERAL, "Waiting for reply... (timeo %d)", + tv.tv_sec); + selret = select(FD_SETSIZE, &readfds, + (void *) 0, (void *) 0, &tv); + } else { +- dprintf(N_DEBUG, "Waiting for client connections."); ++ xlog(D_GENERAL, "Waiting for client connections"); + selret = select(FD_SETSIZE, &readfds, + (void *) 0, (void *) 0, (struct timeval *) 0); + } +@@ -116,8 +116,7 @@ my_svc_run(void) + if (errno == EINTR || errno == ECONNREFUSED + || errno == ENETUNREACH || errno == EHOSTUNREACH) + continue; +- note(N_ERROR, "my_svc_run() - select: %s", +- strerror (errno)); ++ xlog(L_ERROR, "my_svc_run() - select: %m"); + return; + + case 0: diff --git a/nfs-utils.spec b/nfs-utils.spec index b22071f..1afbd34 100644 --- a/nfs-utils.spec +++ b/nfs-utils.spec @@ -2,7 +2,7 @@ Summary: NFS utilities and supporting clients and daemons for the kernel NFS ser Name: nfs-utils URL: http://sourceforge.net/projects/nfs Version: 1.2.1 -Release: 8%{?dist} +Release: 9%{?dist} Epoch: 1 # group all 32bit related archs @@ -22,8 +22,7 @@ Patch00: nfs-utils-1.0.5-statdpath.patch Patch01: nfs-utils-1.1.0-smnotify-path.patch Patch02: nfs-utils-1.1.0-exp-subtree-warn-off.patch -Patch100: nfs-utils-1.2.2-rc3.patch -Patch101: nfs-utils-1.2.1-mount-ipv6-typo.patch +Patch100: nfs-utils-1.2.2-rc5.patch Patch200: nfs-utils-1.2.0-v4root-rel9.patch @@ -78,7 +77,6 @@ This package also contains the mount.nfs and umount.nfs program. %patch02 -p1 %patch100 -p1 -%patch101 -p1 %patch200 -p1 @@ -252,6 +250,9 @@ fi %attr(4755,root,root) /sbin/umount.nfs4 %changelog +* Mon Jan 12 2010 Steve Dickson 1.2.1-9 +- Updated to latest upstream RC release: nfs-utils-1-2-2-rc5 + * Mon Jan 4 2010 Steve Dickson 1.2.1-8 - mount.nfs: don't use IPv6 unless IPV6_SUPPORTED is set