[comp.sources.sun] v01i084: chknfs-1.1 - Test NFS paths for validity

mcgrew@dartagnan.rutgers.edu (Charles Mcgrew) (11/29/89)

Submitted-by: aklietz@ncsa.uiuc.edu (Alan Klietz)
Posting-number: Volume 1, Issue 84
Archive-name: chknfs-1.1


This program is called cknfs.  It checks for dead NFS servers.

I wrote it because I was tired of logging into a NFS client workstation,
only to find myself hung because one of my twenty-odd execution paths
pointed to some obscure NFS server that happened to be dead.

Well, this program fixes that problem.  It takes a list of execution
paths as arguments.   Each path is examined for an NFS mount point.
If found, the corresponding NFS server is checked.   Paths that lead
to dead NFS servers are ignored.  The remaining paths are printed to
stdout. 

Typical examples:

	set path = `cknfs /bin /usr/bin /usr/ucb . /usr6/bin /sdg/bin`
	alias cd 'cknfs -e \!*; if ($status == 0) chdir \!*'

The latter example prevents you from hanging if you cd to a
directory that leads to a dead NFS server.


#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  Makefile README cknfs.c cknfs.man patchlevel.h
# Wrapped by aklietz@occam on Wed Jun 21 00:17:44 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(1092 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X#
X# $Header: Makefile,v 1.1 89/06/20 23:29:37 aklietz Exp $
X#
X# $Log:	Makefile,v $
X# Revision 1.1  89/06/20  23:29:37  aklietz
X# Initial revision
X# 
X#
XSHELL	= /bin/sh
X
X###  Change to -g for debugging
XCFLAGS	= -O
X
X### Necessary include dirs, if any
XINCLUDES =
X#INCLUDES = -I/usr/include/sun -I/usr/include/bsd     # for SGI systems
X
X### Necessary libraries, if any
XLFLAGS=
X#LFLAGS= -lsun -lbsd   # for SGI systems
X
X###  Where executable should be put
XDESTDIR	= /usr/local/bin
X
X###  Where man page should be put
XMANDIR	= /usr/man/manl
X
X###  Suffix for man page
XMANSUFFIX = 1l
X#MANSUFFIX = 1
X#MANSUFFIX = l
X#MANSUFFIX = 1local
X
X###
X###  End of configuration section
X###
X
XMANPAGE = cknfs.$(MANSUFFIX)
XPROG = cknfs
X
Xall:	$(PROG)
X
Xcknfs:	cknfs.c
X	$(CC) $(CFLAGS) $(INCLUDES) cknfs.c -o cknfs $(LFLAGS)
X
Xinstall:	$(PROG)
X	rm -f $(DESTDIR)/$(PROG)
X	cp $(PROG) $(DESTDIR)
X	chmod 755 $(DESTDIR)/$(PROG)
X	rm -f $(MANDIR)/$(MANPAGE)
X	cp cknfs.man $(MANDIR)/$(MANPAGE)
X	chmod 644 $(MANDIR)/$(MANPAGE)
X
Xclean:
X	rm -f *.o core
X
Xclobber:
X	rm -f *.o core $(PROG)
X
Xlint:	cknfs.c
X	lint -ahb $(INCLUDES) cknfs.c
END_OF_FILE
if test 1092 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(795 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XThis program checks for dead NFS servers.
X
XI wrote it because I was tired of logging into a NFS client workstation,
Xonly to find myself hung because one of my twenty-odd execution paths
Xpointed to some obscure NFS server that happened to be dead.
X
XWell, this program fixes that problem.  It takes a list of execution
Xpaths as arguments.   Each path is examined for an NFS mount point.
XIf found, the corresponding NFS server is checked.   Paths that lead
Xto dead NFS servers are ignored.  The remaining paths are printed to
Xstdout.  No more hung logins!
X
XTypical examples:
X
X	set path = `cknfs /bin /usr/bin /usr/ucb . /usr6/bin /sdg/bin`
X	alias cd 'cknfs -e \!*; if ($status == 0) chdir \!*'
X
XThe latter example prevents you from hanging if you cd to a
Xdirectory that leads to a dead NFS server.
END_OF_FILE
if test 795 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'cknfs.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cknfs.c'\"
else
echo shar: Extracting \"'cknfs.c'\" \(13846 characters\)
sed "s/^X//" >'cknfs.c' <<'END_OF_FILE'
X/*
X * cknfs - Check for dead NFS servers
X *
X * Don't you hate it when you login on an NFS client, only to find
X * yourself hung because one of your execution paths points to a dead
X * NFS server?
X *
X * Well, this program fixes that problem.  It takes a list of execution
X * paths as arguments.   Each path is examined for an NFS mount point.
X * If found, the corresponding NFS server is checked.   Paths that lead
X * to dead NFS servers are ignored.  The remaining paths are printed to
X * stdout.  No more hung logins!
X *
X * Usage: cknfs -e -s -t# -v -D -L paths
X *  
X *       -e     silent, do not print paths
X *       -s     print paths in sh format (colons)
X *       -t n   timeout interval before assuming an NFS
X *              server is dead (default 10 seconds)
X *       -v     verbose
X *       -D     debug
X *       -L     expand symbolic links
X *
X * Typical examples:
X *
X *	set path = `cknfs /bin /usr/bin /usr/ucb . /usr6/bin /sdg/bin`
X *	alias cd 'cknfs -e \!*; if ($status == 0) chdir \!*'
X *
X * The latter example prevents you from hanging if you cd to a
X * directory that leads to a dead NFS server.
X *
X * Adminstrative note:  You can still get hung if your administator 
X * mixes NFS mount points from different machines in the same parent
X * directory or if your administrator mixes regular directories and
X * NFS mount points in the same parent directory.
X *
X * The best organization is an overall /nfs directory with subdirectories 
X * for each machine.   For example, if you have 3 NFS servers named
X * "newton", "bardeen", and "zaphod", a good organization would be
X *
X *	/nfs/bardeen/apps
X *	/nfs/bardeen/bin
X *	/nfs/newton/bin
X *	/nfs/newton/local
X *	/nfs/zaphod/bin
X *	/nfs/zaphod/sdg
X *
X * NEVER MIX MOUNT POINTS FROM DIFFERENT MACHINES IN THE SAME
X * PARENT DIRECTORY.
X *
X * Implementation note: A small amount of system-dependent code is required
X * to read the mount table.   This is located in mkm_mlist() at bottom of the
X * program.  It may have to be edited to handle local system dependencies.
X * #ifdef'ed versions for SunOs, Ultrix, and SGI are included.
X */
X
X/*
X * Copyright (c) 1989, The Board of Trustees of the University of Illinois
X * National Center for Supercomputing Applications.
X *
X * No warranty is expressed or implied.
X * Unlimited redistribution permitted.
X *
X */
X
Xstatic char *RCSid = "$Header: cknfs.c,v 1.6 89/06/21 00:04:15 aklietz Exp $";
X
X/*
X * $Log:	cknfs.c,v $
X * Revision 1.6  89/06/21  00:04:15  aklietz
X * Linted.  Baseline for release.
X * 
X * Revision 1.5  89/06/20  23:37:59  aklietz
X * Restart the parse loop on .. instead of just popping the stack,
X * because a/../b need not necessarily == b across a symbolic link.
X * Add support for SGI.
X * 
X * Revision 1.4  89/05/31  18:24:49  aklietz
X * Fix bug introduced in rev 1.3 that did hangable lstat before
X * checking for NFS mount point.
X * Add support for Ultrix.
X * 
X * Revision 1.3  89/05/29  03:30:55  aklietz
X * Terminate silently if no args in -e mode.
X * Fix omission of chdir("/") during parse of symlink to absolute path.
X * 
X * Revision 1.2  89/05/26  14:14:35  aklietz
X * Baseline for release
X * 
X * Revision 1.1  89/05/26  13:37:39  aklietz
X * Initial revision
X * 
X */
X
X#include "sys/param.h"
X#include "errno.h"
X#include "sys/types.h"
X#include "sys/stat.h"
X#include "signal.h"
X#include "ctype.h"
X#include "stdio.h"
X#include "sys/time.h"
X#include "sys/socket.h"
X#include "arpa/inet.h"
X#include "netdb.h"
X#include "rpc/rpc.h"
X#include "rpc/pmap_prot.h"
X#include "rpc/pmap_clnt.h"
X#ifndef sgi /* sgi is missing nfs.h for some reason */
X#include "nfs/nfs.h"
X#else
X	/* sgi is missing nfs.h, so we must hardcode the RPC values */
X#	define NFS_PROGRAM 100003L
X#	define NFS_VERSION 2L
X#endif
X
X/*
X * Make initial program
X * May 1989, Alan Klietz (aklietz@ncsa.uiuc.edu)
X */
X
X#define DEFAULT_TIMEOUT 10  /* Default timeout for checking NFS server */
X
Xextern char *realloc();
Xextern char *strchr(), *strrchr(), *strtok();
X
Xstruct m_mlist {
X	int mlist_checked; /* -1 if bad, 0 if not checked, 1 if ok */
X	struct m_mlist *mlist_next;
X	char *mlist_dir;
X	char *mlist_fsname;
X	int mlist_isnfs;
X};
Xstatic struct m_mlist *firstmnt;
X
Xstatic int errflg;
Xstatic int eflg, sflg, vflg, Dflg, Lflg;
Xstatic int timeout = DEFAULT_TIMEOUT;
Xstatic char prefix[MAXPATHLEN];
Xstruct m_mlist *isnfsmnt();
Xchar *xalloc();
Xvoid mkm_mlist();
X
Xint
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	register int n;
X	register char *s;
X	int good = 0;
X	char outbuf[BUFSIZ];
X	char errbuf[BUFSIZ];
X	extern int optind;
X	extern char *optarg;
X
X
X	/*
X	 * Avoid intermixing stdout and stderr
X	 */
X	setvbuf(stdout, outbuf, _IOFBF, sizeof(outbuf));
X	setvbuf(stderr, errbuf, _IOLBF, sizeof(errbuf));
X
X	while ((n = getopt(argc, argv, "est:vDL")) != EOF)
X		switch(n) {
X			case 'e':	++eflg;
X					break;
X
X			case 's':	++sflg;
X					break;
X
X			case 't':	timeout = atoi(optarg);
X					break;
X
X			case 'v':	++vflg;
X					break;
X
X			case 'D':	++Dflg; ++vflg;
X					break;
X
X			case 'L':	++Lflg;
X					break;
X
X			default:
X					++errflg;
X		}
X
X	if (argc <= optind && !eflg) /* no paths */
X		++errflg;
X
X	if (errflg) {
X		fprintf(stderr, "Usage: %s -e -s -t# -v -D -L paths\n", argv[0]);
X		fprintf(stderr, "\tCheck paths for dead NFS servers\n");
X		fprintf(stderr, "\tGood paths are printed to stdout\n\n");
X		fprintf(stderr, "\t -e\tsilent, do not print paths\n");
X		fprintf(stderr, "\t -s\tprint paths in sh format (semicolons)\n");
X		fprintf(stderr, "\t -t n\ttimeout interval before assuming an NFS\n");
X		fprintf(stderr, "\t\tserver is dead (default 10 seconds)\n");
X		fprintf(stderr, "\t -v\tverbose\n");
X		fprintf(stderr, "\t -D\tdebug\n");
X		fprintf(stderr, "\t -L\texpand symbolic links\n\n");
X		exit(1);
X	}
X
X	for (n = optind; n < argc; ++n) {
X		s = argv[n];
X		if (Dflg)
X			fprintf(stderr, "chkpath(%s)\n", s);
X		if (chkpath(s)) {
X			if (good++ && !eflg)
X				putchar(sflg ? ':' : ' ');
X			if (!eflg)
X				fputs(Lflg ? prefix : s, stdout);
X		} else
X			if (vflg)
X				fprintf(stderr, "path skipped: %s\n",
X					Lflg ? prefix : s);
X	}
X
X	if (good && !eflg)
X		putchar('\n');
X
X	(void) fflush(stderr);
X	(void) fflush(stdout);
X
X	exit(good == 0 && optind < argc );
X}
X
X
Xint chkpath(path)
X/*
X * Check path for accessibility.  Return 1 if ok, 0 if error
X */
Xchar *path;
X{
X	extern char *getwd();
X
X	if (*path != '/') { /* If not absolute path, get initial prefix */
X		if (getwd(prefix) == NULL) {
X			fprintf(stderr, "%s\n", prefix);
X			return 0;
X		}
X	}
X	return(_chkpath(path));
X}
X
X
X#define NTERMS 256
X
Xint
X_chkpath(path)
Xchar *path;
X{
X	register char *s, *s2;
X	register int i, front=0, back=0;
X	struct stat stb;
X	struct m_mlist *mlist;
X	char p[MAXPATHLEN];
X	char symlink[MAXPATHLEN];
X	char *queue[NTERMS];
X
X
X	/*
X	 * Copy path to working storage
X	 */
X	strncpy(p, path, sizeof(p)-1);
X
X	if (*p == '/') { /* If absolute path, start at root */
X		*prefix = '\0';
X		(void) chdir("/");
X	}
X
X	if (Dflg)
X		fprintf(stderr, "_chkpath(%s) prefix=%s\n", path, prefix);
X
X	/*
X	 * Put directory terms on FIFO queue
X	 */
X	for (s = strtok(p, "/"); s != NULL; s = strtok((char *)NULL, "/")) {
X		if (back >= NTERMS) {
X			fprintf(stderr, "Too many subdirs: %s\n", path);
X			goto fail;
X		}
X		queue[back++] = s;
X	}
X	/*  queue[front] = a, queue[front+1] = b, ... queue[back] = null */
X
X	/*
X	 * Scan queue of directory terms, expanding 
X	 * symbolic links recursively.
X	 */
X	while (front != back) {
X		s = queue[front++];
X		/* Dot */
X		if (s[0] == '.' && s[1] == '\0')
X			continue;
X		/* Dot Dot */
X		if (s[0] == '.' && s[1] == '.' && s[2] == '\0') {
X			if (chdir("..") < 0) {
X				perror("chdir(..)");
X				goto fail;
X			}
X			/* Remove trailing component of prefix */
X			if ((s2 = strrchr(prefix, '/')) != NULL)
X				*s2 = '\0';
X			continue;
X		} else {
X			(void) strcat(prefix, "/");
X			(void) strcat(prefix, s);
X		}
X
X		if ((mlist = isnfsmnt(prefix)) != NULL) /* NFS mount? */
X			if (chknfsmnt(mlist) <= 0)
X				return 0;
X
X		/* Check if symlink */
X		if (lstat(s, &stb) < 0) {
X			perror(s);
X			goto fail;
X		}
X		if ((stb.st_mode & S_IFMT) != S_IFLNK) {
X			/* not symlink */
X			if (chdir(s) < 0) {
X				perror(prefix);
X				goto fail;
X			}
X			continue;
X		}
X
X		/* Remove symlink from tail of prefix */
X		if ((s2 = strrchr(prefix, '/')) != NULL)
X			*s2 = '\0';
X		/* 
X		 * Read symlink
X		 */
X		if ((i = readlink(s, symlink, MAXPATHLEN-1)) < 0) {
X			perror(s);
X			goto fail;
X		}
X		symlink[i] = '\0'; /* null terminate */
X
X		/*
X		 * Recursively check symlink
X		 */
X		if (_chkpath(symlink) == 0)
X			return 0;
X	}
X
X	return 1;
X
Xfail:
X	return 0;
X}
X	
X
Xstruct m_mlist *
Xisnfsmnt(path)
X/*
X * Return 1 if path is NFS mount point
X */
Xchar *path;
X{
X	register struct m_mlist *mlist;
X	static int init;
X
X	if (init == 0) {
X		++init;
X		mkm_mlist();
X	}
X
X	for (mlist = firstmnt; mlist != NULL; mlist = mlist->mlist_next) {
X		if (mlist->mlist_isnfs == 0)
X			continue;
X		if (strcmp(mlist->mlist_dir, path) == 0)
X			return(mlist);
X	}
X	return NULL;
X}
X
X
Xstatic int
Xget_inaddr(saddr, host)
X/*
X * Translate host name to Internet address.
X * Return 1 if ok, 0 if error
X */
Xstruct sockaddr_in *saddr;
Xchar *host;
X{
X	register struct hostent *hp;
X
X	(void) memset((char *)saddr, 0, sizeof(struct sockaddr_in));
X	saddr->sin_family = AF_INET;
X	if ((saddr->sin_addr.s_addr = inet_addr(host)) == -1) {
X		if ((hp = gethostbyname(host)) == NULL) {
X			fprintf(stderr, "%s: unknown host\n", host);
X			return 0;
X		}
X		(void) memcpy((char *)&saddr->sin_addr, hp->h_addr,
X			hp->h_length);
X	}
X	return 1;
X}
X
X
Xint
Xchknfsmnt(mlist)
X/*
X * Ping the NFS server indicated by the given mnt entry
X */
Xregister struct m_mlist *mlist;
X{
X	register char *s;
X	register struct m_mlist *mlist2;
X	CLIENT *client;
X	struct sockaddr_in saddr;
X	int sock, len;
X	struct timeval tottimeout;
X	struct timeval interval;
X	unsigned short port = 0;
X	struct pmap pmap;
X	enum clnt_stat rpc_stat;
X	static char p[MAXPATHLEN];
X
X	if (Dflg)
X		fprintf(stderr, "chknfsmnt(%s)\n", mlist->mlist_fsname);
X
X	if (mlist->mlist_checked) /* if already checked this mount point */
X		return (mlist->mlist_checked);
X
X	/*
X	 * Save path to working storage and strip colon
X	 */
X	(void) strncpy(p, mlist->mlist_fsname, sizeof(p)-1);
X	if ((s = strchr(p, ':')) != NULL)
X		*s = '\0';
X	len = strlen(p);
X
X	/*
X	 * See if remote host already checked via another mount point
X	 */
X	for (mlist2 = firstmnt; mlist2 != NULL; mlist2 = mlist2->mlist_next)
X		if (strncmp(mlist2->mlist_fsname, p, len) == 0 
X				&& mlist2->mlist_checked)
X			return(mlist2->mlist_checked);
X
X	mlist->mlist_checked = -1; /* set failed */
X	if (vflg)
X		fprintf(stderr, "Checking %s..\n", p);
X	interval.tv_sec = 2;  /* retry interval */
X	interval.tv_usec = 0;
X
X	/*
X	 * Parse internet address
X	 */
X	if (get_inaddr(&saddr, p) == 0)
X		return 0;
X	/*
X	 * Get socket to remote portmapper
X	 */
X	saddr.sin_port = htons(PMAPPORT);
X	sock = RPC_ANYSOCK;
X	if ((client = clntudp_create(&saddr, PMAPPROG, PMAPVERS, interval, 
X			&sock)) == NULL) {
X		clnt_pcreateerror(p);
X		return 0;
X	}
X	/*
X	 * Query portmapper for port # of NFS server
X	 */
X	pmap.pm_prog = NFS_PROGRAM;
X	pmap.pm_vers = NFS_VERSION;
X	pmap.pm_prot = IPPROTO_UDP;
X	pmap.pm_port = 0;
X	tottimeout.tv_sec = timeout;  /* total timeout */
X	tottimeout.tv_usec = 0;
X	if ((rpc_stat = clnt_call(client, PMAPPROC_GETPORT, xdr_pmap, &pmap,
X			xdr_u_short, &port, tottimeout)) != RPC_SUCCESS) {
X		clnt_perror(client, p);
X		clnt_destroy(client);
X		return 0;
X	}
X	clnt_destroy(client);
X
X	if (port == 0) {
X		fprintf(stderr, "%s: NFS server not registered\n", p);
X		return 0;
X	}
X	/*
X	 * Get socket to NFS server
X	 */
X	saddr.sin_port = htons(port);
X	sock = RPC_ANYSOCK;
X	if ((client = clntudp_create(&saddr, NFS_PROGRAM, NFS_VERSION,
X			interval, &sock)) == NULL) {
X		clnt_pcreateerror(p);
X		return 0;
X	}
X	/*
X	 * Ping NFS server
X	 */
X	tottimeout.tv_sec = timeout;
X	tottimeout.tv_usec = 0;
X	if ((rpc_stat = clnt_call(client, NULLPROC, xdr_void, (char *)NULL,
X			xdr_void, (char *)NULL, tottimeout)) != RPC_SUCCESS) {
X		clnt_perror(client, p);
X		clnt_destroy(client);
X		return 0;
X	}
X	clnt_destroy(client);
X	mlist->mlist_checked = 1; /* set success */
X	if (vflg)
X		fprintf(stderr, "%s ok\n", p);
X	return 1;
X}
X
X
Xchar *
Xxalloc(size)
X/*
X * Alloc memory with error checks
X */
Xint size;
X{
X	register char *mem;
X	char *malloc();
X	
X	if ((mem = (char *)malloc((unsigned)size)) == NULL) {
X		(void) fprintf(stderr, "out of memory\n");
X		exit(1);
X	}
X	return(mem);
X}
X
X/*
X * Begin machine dependent code for mount table 
X */
X
X#if defined(sun) || defined(sgi)
X#include <mntent.h>
Xvoid
Xmkm_mlist()
X/*
X * Build list of mnt entries - Sun version
X */
X{
X	FILE *mounted;
X	struct m_mlist *mlist;
X	struct mntent *mnt;
X
X	if ((mounted = setmntent(MOUNTED, "r"))== NULL) {
X		perror(MOUNTED);
X		exit(1);
X	}
X	while ((mnt = getmntent(mounted)) != NULL) {
X		mlist = (struct m_mlist *)xalloc(sizeof(*mlist));
X		mlist->mlist_next = firstmnt;
X		mlist->mlist_checked = 0;
X		mlist->mlist_dir = xalloc(strlen(mnt->mnt_dir)+1);
X		(void) strcpy(mlist->mlist_dir, mnt->mnt_dir);
X		mlist->mlist_fsname = xalloc(strlen(mnt->mnt_fsname)+1);
X		(void) strcpy(mlist->mlist_fsname, mnt->mnt_fsname);
X		mlist->mlist_isnfs = !strcmp(mnt->mnt_type, MNTTYPE_NFS);
X		firstmnt = mlist;
X	}
X	(void) endmntent(mounted);
X}
X#endif
X
X#if defined(ultrix)
X#include <sys/fs_types.h>
X#include <sys/mount.h>
Xvoid
Xmkm_mlist()
X/*
X * Build list of mnt entries - Ultrix version
X */
X{
X	struct m_mlist *mlist;
X	struct fs_data fs_data;
X	int start=0, len;
X
X	while ((len = getmnt(&start, &fs_data, sizeof(fs_data), 
X			NOSTAT_MANY, NULL)) > 0) {
X		mlist = (struct m_mlist *)xalloc(sizeof(*mlist));
X		mlist->mlist_next = firstmnt;
X		mlist->mlist_checked = 0;
X		mlist->mlist_dir = xalloc(strlen(fs_data.fd_path)+1);
X		(void) strcpy(mlist->mlist_dir, fs_data.fd_path);
X		mlist->mlist_fsname = 
X			xalloc(strlen(fs_data.fd_devname)+1);
X		(void) strcpy(mlist->mlist_fsname, fs_data.fd_devname);
X		mlist->mlist_isnfs = (fs_data.fd_fstype == GT_NFS);
X		firstmnt = mlist;
X	}
X	if (len < 0) {
X		perror("getmnt");
X		exit(1);
X	}
X}
X#endif
END_OF_FILE
if test 13846 -ne `wc -c <'cknfs.c'`; then
    echo shar: \"'cknfs.c'\" unpacked with wrong size!
fi
# end of 'cknfs.c'
fi
if test -f 'cknfs.man' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cknfs.man'\"
else
echo shar: Extracting \"'cknfs.man'\" \(1718 characters\)
sed "s/^X//" >'cknfs.man' <<'END_OF_FILE'
X.TH CKNFS 1L 06/10/89 NCSA
X.SH NAME
Xcknfs \- check for dead NFS servers
X.SH SYNOPSIS
X.B cknfs
X[ \fB-esvDL\fR ] [ \fB-t \fItimeout\fR ] [path...]
X.SH DESCRIPTION
X.I Cknfs
Xtakes a list of execution paths.  Each path is examined
Xfor an NFS mount point.  If found, the corresponding NFS server
Xis checked.  Paths that lead to dead NFS servers are ignored.
XThe remaining paths are printed to stdout.
X.SS Options
X.PP
XThe following options are available,
X.TP
X\fB-e\fR
XSilent.  Do not print paths.
X.TP
X\fB-s\fR
XPrint paths in
X.I sh
Xformat, with colons as separators.
X.TP
X\fB-t \fItimeout\fR
XSpecify the timeout interval before assuming an NFS server is dead.
XThe default is 10 seconds.
X.TP
X\fB-v\fR
XVerbose.  A status message is printed for each NFS server.
X.TP
X\fB-D\fR
XDebug.  Messages are printed as the paths are parsed.
X.TP
X\fB-L\fR
XExpand symbolic links on output.  This increases the efficiency of shell path
Xsearches on machines without a kernel directory name cache.
X.sp
X.SH EXAMPLES
X.sp
X.RS
Xset path = `/usr/lbin/cknfs /bin /usr/bin /usr/ucb . /usr6/bin /sdg/bin`
X.RE
X.sp
X.RS
Xalias cd 'cknfs \-e \e!*; if ($status == 0) chdir \e!*'
X.RE
X.sp
XThe latter example checks the path before performing a
X.I chdir
Xoperation.
X.SH "SEE ALSO"
Xnfs(4)
X.SH AUTHOR
X.nf
XAlan Klietz
XNational Center for Supercomputing Applications
X.fi
X.SH BUGS
XIn some instances, a
Xpath that
X.I cknfs
Xassumes valid could be vulnerable to hanging if a 3rd party machine
Xfails.  This happens if your administrator mixes NFS mount points
Xand/or regular directories with NFS directories from
X3rd party machines.  The best organization is an overall
X.I /nfs
Xdirectory with local subdirectories for each server machine and with
Xmount points located therein.
END_OF_FILE
if test 1718 -ne `wc -c <'cknfs.man'`; then
    echo shar: \"'cknfs.man'\" unpacked with wrong size!
fi
# end of 'cknfs.man'
fi
if test -f 'patchlevel.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'patchlevel.h'\"
else
echo shar: Extracting \"'patchlevel.h'\" \(190 characters\)
sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE'
X/*
X * $Header: patchlevel.h,v 1.1 89/06/20 21:48:53 aklietz Exp $
X *
X * $Log:	patchlevel.h,v $
X * Revision 1.1  89/06/20  21:48:53  aklietz
X * Initial revision
X * 
X */
X
X#define PATCHLEVEL 1
END_OF_FILE
if test 190 -ne `wc -c <'patchlevel.h'`; then
    echo shar: \"'patchlevel.h'\" unpacked with wrong size!
fi
# end of 'patchlevel.h'
fi
echo shar: End of shell archive.
exit 0