diff --git a/SOURCES/nfs-utils-2.3.3-nfsclnts-cmd.patch b/SOURCES/nfs-utils-2.3.3-nfsclnts-cmd.patch new file mode 100644 index 0000000..9363c2a --- /dev/null +++ b/SOURCES/nfs-utils-2.3.3-nfsclnts-cmd.patch @@ -0,0 +1,481 @@ +diff -up nfs-utils-2.3.3/configure.ac.orig nfs-utils-2.3.3/configure.ac +--- nfs-utils-2.3.3/configure.ac.orig 2020-06-09 10:58:50.178258035 -0400 ++++ nfs-utils-2.3.3/configure.ac 2020-06-09 11:02:04.203102954 -0400 +@@ -639,6 +639,7 @@ AC_CONFIG_FILES([ + tools/rpcgen/Makefile + tools/mountstats/Makefile + tools/nfs-iostat/Makefile ++ tools/nfsdclnts/Makefile + tools/nfsconf/Makefile + tools/nfsdclddb/Makefile + utils/Makefile +diff -up nfs-utils-2.3.3/tools/Makefile.am.orig nfs-utils-2.3.3/tools/Makefile.am +--- nfs-utils-2.3.3/tools/Makefile.am.orig 2020-06-09 10:58:50.178258035 -0400 ++++ nfs-utils-2.3.3/tools/Makefile.am 2020-06-09 11:02:04.203102954 -0400 +@@ -12,6 +12,6 @@ if CONFIG_NFSDCLD + OPTDIRS += nfsdclddb + endif + +-SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat $(OPTDIRS) ++SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat nfsdclnts $(OPTDIRS) + + MAINTAINERCLEANFILES = Makefile.in +diff -up nfs-utils-2.3.3/tools/nfsdclnts/Makefile.am.orig nfs-utils-2.3.3/tools/nfsdclnts/Makefile.am +--- nfs-utils-2.3.3/tools/nfsdclnts/Makefile.am.orig 2020-06-09 11:02:04.203102954 -0400 ++++ nfs-utils-2.3.3/tools/nfsdclnts/Makefile.am 2020-06-09 11:02:04.203102954 -0400 +@@ -0,0 +1,13 @@ ++## Process this file with automake to produce Makefile.in ++PYTHON_FILES = nfsdclnts.py ++ ++man8_MANS = nfsdclnts.man ++ ++EXTRA_DIST = $(man8_MANS) $(PYTHON_FILES) ++ ++all-local: $(PYTHON_FILES) ++ ++install-data-hook: ++ $(INSTALL) -m 755 nfsdclnts.py $(DESTDIR)$(sbindir)/nfsdclnts ++ ++MAINTAINERCLEANFILES=Makefile.in +diff -up nfs-utils-2.3.3/tools/nfsdclnts/nfsdclnts.man.orig nfs-utils-2.3.3/tools/nfsdclnts/nfsdclnts.man +--- nfs-utils-2.3.3/tools/nfsdclnts/nfsdclnts.man.orig 2020-06-09 11:02:04.203102954 -0400 ++++ nfs-utils-2.3.3/tools/nfsdclnts/nfsdclnts.man 2020-06-09 11:02:04.203102954 -0400 +@@ -0,0 +1,180 @@ ++.\" ++.\" nfsdclnts(8) ++.\" ++.TH "NFSDCLTS" "8" "2020-05-09" "nfsdclnts" "nfsdclnts" ++.ie \n(.g .ds Aq \(aq ++.el .ds Aq ' ++.ss \n[.ss] 0 ++.nh ++.ad l ++.de URL ++\fI\\$2\fP <\\$1>\\$3 ++.. ++.als MTO URL ++.if \n[.g] \{\ ++. mso www.tmac ++. am URL ++. ad l ++. . ++. am MTO ++. ad l ++. . ++. LINKSTYLE blue R < > ++.\} ++.SH "NAME" ++nfsdclnts \- print various nfs client information for knfsd server. ++.SH "SYNOPSIS" ++.sp ++\fBnfsdclnts\fP [\fI\-h\fP] [\fI\-t type\fP] [\fI\-\-clientinfo\fP] [\fI\-\-hostname\fP] [\fI\-q\fP] ++.SH "DESCRIPTION" ++.sp ++The nfsdclnts(8) command parses the content present in /proc/fs/nfsd/clients/ directories. nfsdclnts(8) displays files which are open, locked, delegated by the nfs\-client. It also prints useful client information such as hostname, clientID, NFS version mounted by the nfs\-client. ++.SH "OPTIONS" ++.sp ++\fB\-t, \-\-type\fP=TYPE ++.RS 4 ++Specify the type of file to be displayed. Takes only one TYPE at a time. ++.sp ++\fIopen\fP, \fIlock\fP, \fIdeleg\fP, \fIlayout\fP, or \fIall\fP ++.sp ++open: displays the open files by nfs\-client(s). ++.sp ++lock: displays the files locked by nfs\-client(s). ++.sp ++layout: displays the files for which layout is given. ++.sp ++deleg: displays delegated files information and delegation type. ++.sp ++all: prints all the above type. ++.RE ++.sp ++\fB\-\-clientinfo\fP ++.RS 4 ++displays various nfs\-client info fields such as version of nfs mounted at nfs\-client and clientID. ++.RE ++.sp ++\fB\-\-hostname\fP ++.RS 4 ++Print hostname of nfs\-client instead of ip-address. ++.RE ++.sp ++\fB\-q, \-\-quiet\fP ++.RS 4 ++Hide the header information. ++.RE ++.sp ++\fB\-v, \-\-verbose\fP ++.RS 4 ++Verbose operation, show debug messages. ++.RE ++.sp ++\fB\-f, \-\-file\fP ++.RS 4 ++Instead of processing all client directories under /proc/fs/nfsd/clients, one can provide a specific ++states file to process. One should make sure that info file resides in the same directory as states file. ++If the info file is not valid or present the fields would be marked as "N/A". ++.RE ++.sp ++\fB\-h, \-\-help\fP ++.RS 4 ++Print help explaining the command line options. ++.SH "EXAMPLES" ++.sp ++\fBnfsdclnts \-\-type open\fP ++.RS 4 ++List all files with open type only. ++.RE ++.sp ++.if n .RS 4 ++.nf ++Inode number | Type | Access | Deny | ip address | Filename ++33823232 | open | r\- | \-\- | [::1]:757 | testfile ++.fi ++.if n .RE ++.sp ++\fBnfsdclnts \-\-type deleg\fP ++.RS 4 ++List all files with deleg type only. ++.RE ++.sp ++.if n .RS 4 ++.nf ++Inode number | Type | Access | ip address | Filename ++33823232 | deleg | r | [::1]:757 | testfile ++.fi ++.if n .RE ++.sp ++\fBnfsdclnts \-\-hostname\fP ++.RS 4 ++Print hostname instead of ip\-address. ++.RE ++.sp ++.if n .RS 4 ++.nf ++Inode number | Type | Access | Deny | Hostname | Filename ++33823232 | open | r\- | \-\- | nfs\-server | testfile ++33823232 | deleg | r | | nfs\-server | testfile ++.fi ++.if n .RE ++.sp ++\fBnfsdclnts \-\-clientinfo\fP ++.RS 4 ++Print client information. ++.RE ++.sp ++.if n .RS 4 ++.nf ++Inode number | Type | Access | Deny | ip address | Client ID | vers | Filename ++33823232 | open | r\- | \-\- | [::1]:757 | 0xc79a009f5eb65e84 | 4.2 | testfile ++33823232 | deleg | r | | [::1]:757 | 0xc79a009f5eb65e84 | 4.2 | testfile ++.fi ++.if n .RE ++.sp ++\fBnfsdclnts \-\-file /proc/fs/nfsd/clients/3/states -t open\fP ++.RS 4 ++Process specific states file. ++.RE ++.sp ++.if n .RS 4 ++.nf ++Inode number | Type | Access | Deny | ip address | Client ID | vers | Filename ++33823232 | open | r\- | \-\- | [::1]:757 | 0xc79a009f5eb65e84 | 4.2 | testfile ++.fi ++.if n .RE ++.sp ++\fBnfsdclnts \-\-quiet \-\-hostname\fP ++.RS 4 ++Hide the header information. ++.RE ++.sp ++.if n .RS 4 ++.nf ++33823232 | open | r\- | \-\- | nfs\-server | testfile ++33823232 | deleg | r | | nfs\-server | testfile ++.fi ++.if n .RE ++.SH "FILES" ++.sp ++\fB/proc/fs/nfsd/clients/\fP ++.sp ++Displays basic information about each NFSv4 client. ++.sp ++\fB/proc/fs/nfsd/clients/#/info\fP ++.sp ++Displays information about all the opens held by the given client, including open modes, device numbers, inode numbers, and open owners. ++.sp ++\fB/proc/fs/nfsd/clients/#/states\fP ++.SH "NOTES" ++.sp ++/proc/fs/nfsd/clients/ support was initially introduced in 5.3 kernel and is only implemented for mount points using NFSv4. ++.SH "BUGS" ++Please report any BUGs to \c ++.MTO "linux\-nfs\(atvger.kernel.org" "" "" ++.SH SEE ALSO ++.BR nfsd (8), ++.BR exportfs (8), ++.BR idmapd (8), ++.BR statd (8) ++.SH "AUTHORS" ++Achilles Gaikwad <agaikwad@redhat.com> and ++Kenneth D'souza <kdsouza@redhat.com> +diff -up nfs-utils-2.3.3/tools/nfsdclnts/nfsdclnts.py.orig nfs-utils-2.3.3/tools/nfsdclnts/nfsdclnts.py +--- nfs-utils-2.3.3/tools/nfsdclnts/nfsdclnts.py.orig 2020-06-09 11:02:04.203102954 -0400 ++++ nfs-utils-2.3.3/tools/nfsdclnts/nfsdclnts.py 2020-06-09 11:02:04.203102954 -0400 +@@ -0,0 +1,254 @@ ++#!/usr/bin/python3 ++# -*- python-mode -*- ++''' ++ Copyright (C) 2020 ++ Authors: Achilles Gaikwad <agaikwad@redhat.com> ++ Kenneth D'souza <kdsouza@redhat.com> ++ ++ 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 3 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, see <https://www.gnu.org/licenses/>. ++''' ++ ++import multiprocessing as mp ++import os ++import signal ++import sys ++ ++try: ++ import argparse ++except ImportError: ++ print('%s: Failed to import argparse - make sure argparse is installed!' ++ % sys.argv[0]) ++ sys.exit(1) ++try: ++ import yaml ++except ImportError: ++ print('%s: Failed to import yaml - make sure python3-pyyaml is installed!' ++ % sys.argv[0]) ++ sys.exit(1) ++ ++BBOLD = '\033[1;30;47m' #Bold black text with white background. ++ENDC = '\033[m' #Rest to defaults ++ ++def init_worker(): ++ signal.signal(signal.SIGINT, signal.SIG_IGN) ++ ++# this function converts the info file to a dictionary format, sorta. ++def file_to_dict(path): ++ client_info = {} ++ try: ++ with open(path) as f: ++ for line in f: ++ try: ++ (key, val) = line.split(':', 1) ++ client_info[key] = val.strip() ++ # FIXME: There has to be a better way of converting the info file to a dictionary. ++ except ValueError as reason: ++ if verbose: ++ print('Exception occured, %s' % reason) ++ ++ if len(client_info) == 0 and verbose: ++ print("Provided %s file is not valid" %path) ++ return client_info ++ ++ except OSError as reason: ++ if verbose: ++ print('%s' % reason) ++ ++# this function gets the paths from /proc/fs/nfsd/clients/ ++# returns a list of paths for each client which has nfs-share mounted. ++def getpaths(): ++ path = [] ++ try: ++ dirs = os.listdir('/proc/fs/nfsd/clients/') ++ except OSError as reason: ++ exit('%s' % reason) ++ if len(dirs) !=0: ++ for i in dirs: ++ path.append('/proc/fs/nfsd/clients/' + i + '/states') ++ return (path) ++ else: ++ exit('Nothing to process') ++ ++# A single function to rule them all, in this function we gather all the data ++# from already populated data_list and client_info. ++def printer(data_list, argument): ++ client_info_path = data_list.pop() ++ client_info = file_to_dict(client_info_path) ++ for i in data_list: ++ for key in i: ++ inode = i[key]['superblock'].split(':')[-1] ++ # The ip address is quoted, so we dequote it. ++ try: ++ client_ip = client_info['address'][1:-1] ++ except: ++ client_ip = "N/A" ++ try: ++ # if the nfs-server reboots while the nfs-client holds the files open, ++ # the nfs-server would print the filename as '/'. For such instaces we ++ # print the output as disconnected dentry instead of '/'. ++ if(i[key]['filename']=='/'): ++ fname = 'disconnected dentry' ++ else: ++ fname = i[key]['filename'].split('/')[-1] ++ except KeyError: ++ # for older kernels which do not have the fname patch in kernel, they ++ # won't be able to see the fname field. Therefore post it as N/A. ++ fname = "N/A" ++ otype = i[key]['type'] ++ try: ++ access = i[key]['access'] ++ except: ++ access = '' ++ try: ++ deny = i[key]['deny'] ++ except: ++ deny = '' ++ try: ++ hostname = client_info['name'].split()[-1].split('"')[0] ++ hostname = hostname.split('.')[0] ++ # if the hostname is too long, it messes up with the output being in columns, ++ # therefore we truncate the hostname followed by two '..' as suffix. ++ if len(hostname) > 20: ++ hostname = hostname[0:20] + '..' ++ except: ++ hostname = "N/A" ++ try: ++ clientid = client_info['clientid'] ++ except: ++ clientid = "N/A" ++ try: ++ minorversion = "4." + client_info['minor version'] ++ except: ++ minorversion = "N/A" ++ ++ otype = i[key]['type'] ++ # since some fields do not have deny column, we drop those if -t is either ++ # layout or lock. ++ drop = ['layout', 'lock'] ++ ++ # Printing the output this way instead of a single string which is concatenated ++ # this makes it better to quickly add more columns in future. ++ if(otype == argument.type or argument.type == 'all'): ++ print('%-13s' %inode, end='| ') ++ print('%-7s' %otype, end='| ') ++ if (argument.type not in drop): ++ print('%-7s' %access, end='| ') ++ if (argument.type not in drop and argument.type !='deleg'): ++ print('%-5s' %deny, end='| ') ++ if (argument.hostname == True): ++ print('%-22s' %hostname, end='| ') ++ else: ++ print('%-22s' %client_ip, end='| ') ++ if (argument.clientinfo == True) : ++ print('%-20s' %clientid, end='| ') ++ print('%-5s' %minorversion, end='| ') ++ print(fname) ++ ++def opener(path): ++ try: ++ with open(path, 'r') as nfsdata: ++ try: ++ data = yaml.load(nfsdata, Loader = yaml.BaseLoader) ++ if data is not None: ++ clientinfo = path.rsplit('/', 1)[0] + '/info' ++ data.append(clientinfo) ++ return data ++ except: ++ if verbose: ++ print("Exception occurred, Please make sure %s is a YAML file" %path) ++ ++ except OSError as reason: ++ if verbose: ++ print('%s' % reason) ++ ++def print_cols(argument): ++ title_inode = 'Inode number' ++ title_otype = 'Type' ++ title_access = 'Access' ++ title_deny = 'Deny' ++ title_fname = 'Filename' ++ title_clientID = 'Client ID' ++ title_hostname = 'Hostname' ++ title_ip = 'ip address' ++ title_nfsvers = 'vers' ++ ++ drop = ['lock', 'layout'] ++ print(BBOLD, end='') ++ print('%-13s' %title_inode, end='| ') ++ print('%-7s' %title_otype, end='| ') ++ if (argument.type not in drop): ++ print('%-7s' %title_access, end='| ') ++ if (argument.type not in drop and argument.type !='deleg'): ++ print('%-5s' %title_deny, end='| ') ++ if (argument.hostname == True): ++ print('%-22s' %title_hostname, end='| ') ++ else: ++ print('%-22s' %title_ip, end='| ') ++ if (argument.clientinfo == True): ++ print('%-20s' %title_clientID, end='| ') ++ print('%-5s' %title_nfsvers, end='| ') ++ print(title_fname, end='') ++ print(ENDC) ++ ++def nfsd4_show(): ++ ++ parser = argparse.ArgumentParser(description = 'Parse the nfsd states and clientinfo files.') ++ parser.add_argument('-t', '--type', metavar = 'type', type = str, choices = ['open', ++ 'deleg', 'lock', 'layout', 'all'], ++ default = 'all', ++ help = 'Input the type that you want to be printed: open, lock, deleg, layout, all') ++ parser.add_argument('--clientinfo', action = 'store_true', ++ help = 'output clients information, --hostname is implied.') ++ parser.add_argument('--hostname', action = 'store_true', ++ help = 'print hostname of client instead of its ip address. Longer hostnames are truncated.') ++ parser.add_argument('-v', '--verbose', action = 'store_true', ++ help = 'Verbose operation, show debug messages.') ++ parser.add_argument('-f', '--file', nargs='+', type = str, metavar='', ++ help = 'pass client states file, provided that info file resides in the same directory.') ++ parser.add_argument('-q', '--quiet', action = 'store_true', ++ help = 'don\'t print the header information') ++ ++ args = parser.parse_args() ++ ++ global verbose ++ verbose = False ++ if args.verbose: ++ verbose = True ++ ++ if args.file: ++ paths = args.file ++ else: ++ paths = getpaths() ++ ++ p = mp.Pool(mp.cpu_count(), init_worker) ++ try: ++ result = p.map(opener, paths) ++ ### Drop None entries from list ++ final_result = list(filter(None, result)) ++ p.close() ++ p.join() ++ ++ if len(final_result) !=0 and not args.quiet: ++ print_cols(args) ++ ++ for item in final_result: ++ printer(item, args) ++ ++ except KeyboardInterrupt: ++ print('Caught KeyboardInterrupt, terminating workers') ++ p.terminate() ++ p.join() ++ ++if __name__ == "__main__": ++ nfsd4_show() diff --git a/SPECS/nfs-utils.spec b/SPECS/nfs-utils.spec index 48741a0..04d3e5d 100644 --- a/SPECS/nfs-utils.spec +++ b/SPECS/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://linux-nfs.org/ Version: 2.3.3 -Release: 33%{?dist} +Release: 35%{?dist} Epoch: 1 # group all 32bit related archs @@ -59,6 +59,7 @@ Patch025: nfs-utils-2.3.3-junction-err-fix.patch Patch026: nfs-utils-2.3.3-nfsdcld-upstream-update.patch Patch027: nfs-utils-2.3.3-nconnect-manpage.patch Patch028: nfs-utils-2.3.3-nfsdclddb-rename.patch +Patch029: nfs-utils-2.3.3-nfsclnts-cmd.patch Patch100: nfs-utils-1.2.1-statdpath-man.patch Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch @@ -84,7 +85,7 @@ Provides: start-statd = %{epoch}:%{version}-%{release} License: MIT and GPLv2 and GPLv2+ and BSD Requires: rpcbind, sed, gawk, grep -Requires: kmod, keyutils, quota +Requires: kmod, keyutils, quota, python3-pyyaml BuildRequires: libevent-devel libcap-devel BuildRequires: libtirpc-devel libblkid-devel BuildRequires: krb5-libs >= 1.4 autoconf >= 2.57 openldap-devel >= 2.2 @@ -322,6 +323,7 @@ fi %{_sbindir}/nfsconvert %{_sbindir}/nfsdclddb %{_sbindir}/nfsdcld +%{_sbindir}/nfsdclnts %{_mandir}/*/* %{_pkgdir}/*/* @@ -345,7 +347,13 @@ fi %{_libdir}/libnfsidmap.so %changelog -* Mon May 18 2020 Steve Dickson <steved@redhat.com> 2.3.3-32 +* Wed Jun 10 2020 Steve Dickson <steved@redhat.com> 2.3.3-35 +- Fix dependency problems with nfsdclnts (bz 1841502) + +* Tue Jun 9 2020 Steve Dickson <steved@redhat.com> 2.3.3-34 +- New nfsdclnts command added (bz 1841502) + +* Mon May 18 2020 Steve Dickson <steved@redhat.com> 2.3.3-33 - manpage: Add a description of the 'nconnect' mount option (bz 1761352) - nfsdclddb: Redname clddb-tool to nfsdclddb (bz 1836924)