diff --git a/support/include/conffile.h b/support/include/conffile.h index 132a149..672020a 100644 --- a/support/include/conffile.h +++ b/support/include/conffile.h @@ -34,6 +34,7 @@ #define _CONFFILE_H_ #include +#include struct conf_list_node { TAILQ_ENTRY(conf_list_node) link; diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py index 9626d42..2d0b143 100644 --- a/tools/nfs-iostat/nfs-iostat.py +++ b/tools/nfs-iostat/nfs-iostat.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # -*- python-mode -*- """Emulate iostat for NFS mount points using /proc/self/mountstats """ @@ -21,6 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ import sys, os, time +from optparse import OptionParser, OptionGroup Iostats_version = '0.2' @@ -353,6 +354,12 @@ class DeviceData: print '\t%7.3f' % rtt_per_op, print '\t%7.3f' % exe_per_op + def ops(self, sample_time): + sends = float(self.__rpc_data['rpcsends']) + if sample_time == 0: + sample_time = float(self.__nfs_data['age']) + return (sends / sample_time) + def display_iostats(self, sample_time, which): """Display NFS and RPC stats in an iostat-like way """ @@ -395,33 +402,6 @@ class DeviceData: # Functions # -def print_iostat_help(name): - print 'usage: %s [ [ ] ] [ ] [ ] ' % name - print - print ' Version %s' % Iostats_version - print - print ' Sample iostat-like program to display NFS client per-mount statistics.' - print - print ' The parameter specifies the amount of time in seconds between' - print ' each report. The first report contains statistics for the time since each' - print ' file system was mounted. Each subsequent report contains statistics' - print ' collected during the interval since the previous report.' - print - print ' If the parameter is specified, the value of determines the' - print ' number of reports generated at seconds apart. If the interval' - print ' parameter is specified without the parameter, the command generates' - print ' reports continuously.' - print - print ' Options include "--attr", which displays statistics related to the attribute' - print ' cache, "--dir", which displays statistics related to directory operations,' - print ' and "--page", which displays statistics related to the page cache.' - print ' By default, if no option is specified, statistics related to file I/O are' - print ' displayed.' - print - print ' If one or more names are specified, statistics for only these' - print ' mount points will be displayed. Otherwise, all NFS mount points on the' - print ' client are listed.' - def parse_stats_file(filename): """pop the contents of a mountstats file into a dictionary, keyed by mount point. each value object is a list of the @@ -446,109 +426,198 @@ def parse_stats_file(filename): return ms_dict -def print_iostat_summary(old, new, devices, time, ac): - for device in devices: - stats = DeviceData() - stats.parse_stats(new[device]) - if not old: - stats.display_iostats(time, ac) - else: +def print_iostat_summary(old, new, devices, time, options): + stats = {} + diff_stats = {} + + if old: + # Trim device list to only include intersection of old and new data, + # this addresses umounts due to autofs mountpoints + devicelist = filter(lambda x:x in devices,old) + else: + devicelist = devices + + for device in devicelist: + stats[device] = DeviceData() + stats[device].parse_stats(new[device]) + if old: old_stats = DeviceData() old_stats.parse_stats(old[device]) - diff_stats = stats.compare_iostats(old_stats) - diff_stats.display_iostats(time, ac) + diff_stats[device] = stats[device].compare_iostats(old_stats) + + if options.sort: + if old: + # We now have compared data and can print a comparison + # ordered by mountpoint ops per second + devicelist.sort(key=lambda x: diff_stats[x].ops(time), reverse=True) + else: + # First iteration, just sort by newly parsed ops/s + devicelist.sort(key=lambda x: stats[x].ops(time), reverse=True) + + count = 1 + for device in devicelist: + if old: + diff_stats[device].display_iostats(time, options.which) + else: + stats[device].display_iostats(time, options.which) + + count += 1 + if (count > options.list): + return + + +def list_nfs_mounts(givenlist, mountstats): + """return a list of NFS mounts given a list to validate or + return a full list if the given list is empty - + may return an empty list if none found + """ + list = [] + if len(givenlist) > 0: + for device in givenlist: + stats = DeviceData() + stats.parse_stats(mountstats[device]) + if stats.is_nfs_mountpoint(): + list += [device] + else: + for device, descr in mountstats.iteritems(): + stats = DeviceData() + stats.parse_stats(descr) + if stats.is_nfs_mountpoint(): + list += [device] + return list def iostat_command(name): """iostat-like command for NFS mount points """ mountstats = parse_stats_file('/proc/self/mountstats') devices = [] - which = 0 + origdevices = [] interval_seen = False count_seen = False - for arg in sys.argv: - if arg in ['-h', '--help', 'help', 'usage']: - print_iostat_help(name) - return - - if arg in ['-v', '--version', 'version']: - print '%s version %s' % (name, Iostats_version) - return - - if arg in ['-a', '--attr']: - which = 1 - continue - - if arg in ['-d', '--dir']: - which = 2 - continue - - if arg in ['-p', '--page']: - which = 3 - continue + mydescription= """ +Sample iostat-like program to display NFS client per-mount' +statistics. The parameter specifies the amount of time in seconds +between each report. The first report contains statistics for the time since +each file system was mounted. Each subsequent report contains statistics +collected during the interval since the previous report. If the +parameter is specified, the value of determines the number of reports +generated at seconds apart. If the interval parameter is specified +without the parameter, the command generates reports continuously. +If one or more names are specified, statistics for only these +mount points will be displayed. Otherwise, all NFS mount points on the +client are listed. +""" + parser = OptionParser( + usage="usage: %prog [ [ ] ] [ ] [ ]", + description=mydescription, + version='version %s' % Iostats_version) + parser.set_defaults(which=0, sort=False, list=sys.maxint) + + statgroup = OptionGroup(parser, "Statistics Options", + 'File I/O is displayed unless one of the following is specified:') + statgroup.add_option('-a', '--attr', + action="store_const", + dest="which", + const=1, + help='displays statistics related to the attribute cache') + statgroup.add_option('-d', '--dir', + action="store_const", + dest="which", + const=2, + help='displays statistics related to directory operations') + statgroup.add_option('-p', '--page', + action="store_const", + dest="which", + const=3, + help='displays statistics related to the page cache') + parser.add_option_group(statgroup) + displaygroup = OptionGroup(parser, "Display Options", + 'Options affecting display format:') + displaygroup.add_option('-s', '--sort', + action="store_true", + dest="sort", + help="Sort NFS mount points by ops/second") + displaygroup.add_option('-l','--list', + action="store", + type="int", + dest="list", + help="only print stats for first LIST mount points") + parser.add_option_group(displaygroup) + + (options, args) = parser.parse_args(sys.argv) + + for arg in args: if arg == sys.argv[0]: continue if arg in mountstats: - devices += [arg] + origdevices += [arg] elif not interval_seen: - interval = int(arg) + try: + interval = int(arg) + except: + print 'Illegal value %s' % arg + return if interval > 0: interval_seen = True else: - print 'Illegal value' + print 'Illegal value %s' % arg return elif not count_seen: - count = int(arg) + try: + count = int(arg) + except: + print 'Ilegal value %s' % arg + return if count > 0: count_seen = True else: - print 'Illegal value' + print 'Illegal value %s' % arg return # make certain devices contains only NFS mount points - if len(devices) > 0: - check = [] - for device in devices: - stats = DeviceData() - stats.parse_stats(mountstats[device]) - if stats.is_nfs_mountpoint(): - check += [device] - devices = check - else: - for device, descr in mountstats.iteritems(): - stats = DeviceData() - stats.parse_stats(descr) - if stats.is_nfs_mountpoint(): - devices += [device] + devices = list_nfs_mounts(origdevices, mountstats) if len(devices) == 0: print 'No NFS mount points were found' return + old_mountstats = None sample_time = 0.0 if not interval_seen: - print_iostat_summary(old_mountstats, mountstats, devices, sample_time, which) + print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options) return if count_seen: while count != 0: - print_iostat_summary(old_mountstats, mountstats, devices, sample_time, which) + print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options) old_mountstats = mountstats time.sleep(interval) sample_time = interval mountstats = parse_stats_file('/proc/self/mountstats') + # automount mountpoints add and drop, if automount is involved + # we need to recheck the devices list when reparsing + devices = list_nfs_mounts(origdevices,mountstats) + if len(devices) == 0: + print 'No NFS mount points were found' + return count -= 1 else: while True: - print_iostat_summary(old_mountstats, mountstats, devices, sample_time, which) + print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options) old_mountstats = mountstats time.sleep(interval) sample_time = interval mountstats = parse_stats_file('/proc/self/mountstats') + # automount mountpoints add and drop, if automount is involved + # we need to recheck the devices list when reparsing + devices = list_nfs_mounts(origdevices,mountstats) + if len(devices) == 0: + print 'No NFS mount points were found' + return # # Main diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c index 02239d2..37e2aa5 100644 --- a/utils/gssd/gssd_proc.c +++ b/utils/gssd/gssd_proc.c @@ -840,42 +840,48 @@ handle_krb5_upcall(struct clnt_info *clp) } if (create_resp != 0) { if (uid == 0 && root_uses_machine_creds == 1) { + int nocache = 0; int success = 0; - - gssd_refresh_krb5_machine_credential(clp->servername, - NULL); - /* - * Get a list of credential cache names and try each - * of them until one works or we've tried them all - */ - if (gssd_get_krb5_machine_cred_list(&credlist)) { - printerr(0, "ERROR: No credentials found " - "for connection to server %s\n", - clp->servername); - goto out_return_error; - } - for (ccname = credlist; ccname && *ccname; ccname++) { - gssd_setup_krb5_machine_gss_ccache(*ccname); - if ((create_auth_rpc_client(clp, &rpc_clnt, - &auth, uid, - AUTHTYPE_KRB5)) == 0) { - /* Success! */ - success++; - break; + do { + gssd_refresh_krb5_machine_credential(clp->servername, + NULL, nocache); + /* + * Get a list of credential cache names and try each + * of them until one works or we've tried them all + */ + if (gssd_get_krb5_machine_cred_list(&credlist)) { + printerr(0, "ERROR: No credentials found " + "for connection to server %s\n", + clp->servername); + goto out_return_error; } - printerr(2, "WARNING: Failed to create krb5 context " - "for user with uid %d with credentials " - "cache %s for server %s\n", - uid, *ccname, clp->servername); - } - gssd_free_krb5_machine_cred_list(credlist); - if (!success) { - printerr(1, "WARNING: Failed to create krb5 context " - "for user with uid %d with any " - "credentials cache for server %s\n", - uid, clp->servername); - goto out_return_error; - } + for (ccname = credlist; ccname && *ccname; ccname++) { + gssd_setup_krb5_machine_gss_ccache(*ccname); + if ((create_auth_rpc_client(clp, &rpc_clnt, + &auth, uid, + AUTHTYPE_KRB5)) == 0) { + /* Success! */ + success++; + break; + } + printerr(2, "WARNING: Failed to create machine krb5 context " + "with credentials cache %s for server %s\n", + *ccname, clp->servername); + } + gssd_free_krb5_machine_cred_list(credlist); + if (!success) { + if(nocache == 0) { + nocache++; + printerr(2, "WARNING: Machine cache is prematurely expired or corrupted " + "trying to recreate cache for server %s\n", clp->servername); + } else { + printerr(1, "WARNING: Failed to create machine krb5 context " + "with any credentials cache for server %s\n", + clp->servername); + goto out_return_error; + } + } + } while(!success); } else { printerr(1, "WARNING: Failed to create krb5 context " "for user with uid %d for server %s\n", diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c index 3009cc5..78e9775 100644 --- a/utils/gssd/krb5_util.c +++ b/utils/gssd/krb5_util.c @@ -137,7 +137,7 @@ static int select_krb5_ccache(const struct dirent *d); static int gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d); static int gssd_get_single_krb5_cred(krb5_context context, - krb5_keytab kt, struct gssd_k5_kt_princ *ple); + krb5_keytab kt, struct gssd_k5_kt_princ *ple, int nocache); static int query_krb5_ccache(const char* cred_cache, char **ret_princname, char **ret_realm); @@ -359,7 +359,8 @@ limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid) static int gssd_get_single_krb5_cred(krb5_context context, krb5_keytab kt, - struct gssd_k5_kt_princ *ple) + struct gssd_k5_kt_princ *ple, + int nocache) { #if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS krb5_get_init_creds_opt *init_opts = NULL; @@ -379,7 +380,7 @@ gssd_get_single_krb5_cred(krb5_context context, memset(&my_creds, 0, sizeof(my_creds)); - if (ple->ccname && ple->endtime > now) { + if (ple->ccname && ple->endtime > now && !nocache) { printerr(2, "INFO: Credentials in CC '%s' are good until %d\n", ple->ccname, ple->endtime); code = 0; @@ -1095,7 +1096,7 @@ gssd_get_krb5_machine_cred_list(char ***list) 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); + retval = gssd_refresh_krb5_machine_credential(NULL, ple, 0); if (retval) continue; if (i + 1 > listsize) { @@ -1185,7 +1186,7 @@ gssd_destroy_krb5_machine_creds(void) */ int gssd_refresh_krb5_machine_credential(char *hostname, - struct gssd_k5_kt_princ *ple) + struct gssd_k5_kt_princ *ple, int nocache) { krb5_error_code code = 0; krb5_context context; @@ -1240,7 +1241,7 @@ gssd_refresh_krb5_machine_credential(char *hostname, goto out; } } - retval = gssd_get_single_krb5_cred(context, kt, ple); + retval = gssd_get_single_krb5_cred(context, kt, ple, nocache); out: if (kt) krb5_kt_close(context, kt); diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h index 3d39300..4b6b281 100644 --- a/utils/gssd/krb5_util.h +++ b/utils/gssd/krb5_util.h @@ -30,7 +30,7 @@ void gssd_free_krb5_machine_cred_list(char **list); 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); + struct gssd_k5_kt_princ *ple, int nocache); char *gssd_k5_err_msg(krb5_context context, krb5_error_code code); void gssd_k5_get_default_realm(char **def_realm); diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c index 65a6a2a..573abaa 100644 --- a/utils/idmapd/idmapd.c +++ b/utils/idmapd/idmapd.c @@ -139,6 +139,7 @@ static void nametoidres(struct idmap_msg *); static int nfsdopen(void); static int nfsdopenone(struct idmap_client *); +static void nfsdreopen_one(struct idmap_client *); static void nfsdreopen(void); size_t strlcat(char *, const char *, size_t); @@ -502,7 +503,8 @@ nfsdcb(int fd, short which, void *data) xlog_warn("nfsdcb: read(%s) failed: errno %d (%s)", ic->ic_path, len?errno:0, len?strerror(errno):"End of File"); - goto out; + nfsdreopen_one(ic); + return; } /* Get rid of newline and terminate buffer*/ @@ -514,11 +516,11 @@ nfsdcb(int fd, short which, void *data) /* Authentication name -- ignored for now*/ if (getfield(&bp, authbuf, sizeof(authbuf)) == -1) { xlog_warn("nfsdcb: bad authentication name in upcall\n"); - return; + goto out; } if (getfield(&bp, typebuf, sizeof(typebuf)) == -1) { xlog_warn("nfsdcb: bad type in upcall\n"); - return; + goto out; } if (verbose > 0) xlog_warn("nfsdcb: authbuf=%s authtype=%s", @@ -532,26 +534,26 @@ nfsdcb(int fd, short which, void *data) im.im_conv = IDMAP_CONV_NAMETOID; if (getfield(&bp, im.im_name, sizeof(im.im_name)) == -1) { xlog_warn("nfsdcb: bad name in upcall\n"); - return; + goto out; } break; case IC_IDNAME: im.im_conv = IDMAP_CONV_IDTONAME; if (getfield(&bp, buf1, sizeof(buf1)) == -1) { xlog_warn("nfsdcb: bad id in upcall\n"); - return; + goto out; } tmp = strtoul(buf1, (char **)NULL, 10); im.im_id = (u_int32_t)tmp; if ((tmp == ULONG_MAX && errno == ERANGE) || (unsigned long)im.im_id != tmp) { xlog_warn("nfsdcb: id '%s' too big!\n", buf1); - return; + goto out; } break; default: xlog_warn("nfsdcb: Unknown which type %d", ic->ic_which); - return; + goto out; } imconv(ic, &im); @@ -612,7 +614,7 @@ nfsdcb(int fd, short which, void *data) break; default: xlog_warn("nfsdcb: Unknown which type %d", ic->ic_which); - return; + goto out; } bsiz = sizeof(buf) - bsiz; diff --git a/utils/mount/mount_config.h b/utils/mount/mount_config.h index 9a885a9..3023306 100644 --- a/utils/mount/mount_config.h +++ b/utils/mount/mount_config.h @@ -20,6 +20,8 @@ inline void mount_config_init(char *); #ifdef MOUNT_CONFIG #include "conffile.h" +#include "xlog.h" + extern char *conf_get_mntopts(char *, char *, char *); inline void mount_config_init(char *program) diff --git a/utils/mount/network.c b/utils/mount/network.c index f6fa5fd..bd621be 100644 --- a/utils/mount/network.c +++ b/utils/mount/network.c @@ -90,6 +90,7 @@ static const char *nfs_transport_opttbl[] = { static const char *nfs_version_opttbl[] = { "v2", "v3", + "v4", "vers", "nfsvers", NULL, @@ -1203,7 +1204,7 @@ nfs_nfs_program(struct mount_options *options, unsigned long *program) * Returns TRUE if @version contains a valid value for this option, * or FALSE if the option was specified with an invalid value. */ -static int +int nfs_nfs_version(struct mount_options *options, unsigned long *version) { long tmp; @@ -1215,10 +1216,13 @@ nfs_nfs_version(struct mount_options *options, unsigned long *version) case 1: /* v3 */ *version = 3; return 1; - case 2: /* vers */ + case 2: /* v4 */ + *version = 4; + return 1; + case 3: /* vers */ switch (po_get_numeric(options, "vers", &tmp)) { case PO_FOUND: - if (tmp >= 2 && tmp <= 3) { + if (tmp >= 2 && tmp <= 4) { *version = tmp; return 1; } @@ -1229,10 +1233,10 @@ nfs_nfs_version(struct mount_options *options, unsigned long *version) case PO_BAD_VALUE: return 0; } - case 3: /* nfsvers */ + case 4: /* nfsvers */ switch (po_get_numeric(options, "nfsvers", &tmp)) { case PO_FOUND: - if (tmp >= 2 && tmp <= 3) { + if (tmp >= 2 && tmp <= 4) { *version = tmp; return 1; } diff --git a/utils/mount/network.h b/utils/mount/network.h index db5134c..402e0a5 100644 --- a/utils/mount/network.h +++ b/utils/mount/network.h @@ -56,6 +56,7 @@ int clnt_ping(struct sockaddr_in *, const unsigned long, struct mount_options; +int nfs_nfs_version(struct mount_options *options, unsigned long *version); int nfs_options2pmap(struct mount_options *, struct pmap *, struct pmap *); diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c index f81db14..c5505b1 100644 --- a/utils/mount/nfsumount.c +++ b/utils/mount/nfsumount.c @@ -179,6 +179,10 @@ static int nfs_umount_do_umnt(struct mount_options *options, return EX_FAIL; } + /* Skip UMNT call for vers=4 mounts */ + if (nfs_pmap.pm_vers == 4) + return EX_SUCCESS; + *hostname = nfs_umount_hostname(options, *hostname); if (!*hostname) { nfs_error(_("%s: out of memory"), progname); diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c index a12ace7..3eb661e 100644 --- a/utils/mount/stropts.c +++ b/utils/mount/stropts.c @@ -84,6 +84,7 @@ struct nfsmount_info { struct mount_options *options; /* parsed mount options */ char **extra_opts; /* string for /etc/mtab */ + unsigned long version; /* NFS version */ int flags, /* MS_ flags */ fake, /* actually do the mount? */ child; /* forked bg child? */ @@ -272,7 +273,12 @@ static int nfs_validate_options(struct nfsmount_info *mi) if (!nfs_name_to_address(mi->hostname, sap, &salen)) return 0; - if (strncmp(mi->type, "nfs4", 4) == 0) { + if (!nfs_nfs_version(mi->options, &mi->version)) + return 0; + if (strncmp(mi->type, "nfs4", 4) == 0) + mi->version = 4; + + if (mi->version == 4) { if (!nfs_append_clientaddr_option(sap, salen, mi->options)) return 0; } else { @@ -488,7 +494,7 @@ static int nfs_try_mount(struct nfsmount_info *mi) char *options = NULL; int result; - if (strncmp(mi->type, "nfs4", 4) != 0) { + if (mi->version != 4) { if (!nfs_rewrite_pmap_mount_options(mi->options)) return 0; } diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c index b59f939..888fd8c 100644 --- a/utils/mountd/mountd.c +++ b/utils/mountd/mountd.c @@ -359,6 +359,11 @@ static void set_authflavors(struct mountres3_ok *ok, nfs_export *exp) flavors[i] = s->flav->fnum; i++; } + if (i == 0) { + /* default when there is no sec= option: */ + i = 1; + flavors[0] = AUTH_UNIX; + } ok->auth_flavors.auth_flavors_val = flavors; ok->auth_flavors.auth_flavors_len = i; } diff --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c index 650c593..1cda1e5 100644 --- a/utils/nfsd/nfsd.c +++ b/utils/nfsd/nfsd.c @@ -27,6 +27,15 @@ #include "nfssvc.h" #include "xlog.h" +/* + * IPv6 support for nfsd was finished before some of the other daemons (mountd + * and statd in particular). That could be a problem in the future if someone + * were to boot a kernel that supports IPv6 serving with an older nfs-utils. For + * now, hardcode the IPv6 switch into the off position until the other daemons + * are functional. + */ +#undef IPV6_SUPPORTED + static void usage(const char *); static struct option longopts[] = diff --git a/utils/nfsd/nfssvc.c b/utils/nfsd/nfssvc.c index ee862b2..12d3253 100644 --- a/utils/nfsd/nfssvc.c +++ b/utils/nfsd/nfssvc.c @@ -22,6 +22,15 @@ #include "nfslib.h" #include "xlog.h" +/* + * IPv6 support for nfsd was finished before some of the other daemons (mountd + * and statd in particular). That could be a problem in the future if someone + * were to boot a kernel that supports IPv6 serving with an older nfs-utils. For + * now, hardcode the IPv6 switch into the off position until the other daemons + * are functional. + */ +#undef IPV6_SUPPORTED + #define NFSD_PORTS_FILE "/proc/fs/nfsd/portlist" #define NFSD_VERS_FILE "/proc/fs/nfsd/versions" #define NFSD_THREAD_FILE "/proc/fs/nfsd/threads"