[comp.sources.sun] v01i033: Cknfs - Test NFS paths for validity

mcgrew@dartagnan.rutgers.edu (Charles Mcgrew) (06/22/89)

Submitted-by: aklietz@ncsa.uiuc.edu
Posting-number: Volume 1, Issue 33
Archive-name: cknfs

#! /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:  cknfs.c
# Wrapped by aklietz@occam on Wed May 31 18:25:22 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'cknfs.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cknfs.c'\"
else
echo shar: Extracting \"'cknfs.c'\" \(13080 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 to an NFS client, only to find
X * yourself hung because one of your paths points to a dead NFS server?
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!  Follow this rule and use this program and
X * you will never get hung by NFS again.
X */
X
X/*
X * Copyright (c) 1988, 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.4 89/05/31 18:24:49 aklietz Exp $";
X
X/*
X * $Log:	cknfs.c,v $
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#include <nfs/nfs.h>
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	fflush(stderr);
X	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	if (*path != '/') { /* If not absolute path, get initial prefix */
X		if (getwd(prefix) < 0) {
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(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			strcat(prefix, "/");
X			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				fprintf(stderr, "chdir(%s): ", s);
X				perror("");
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	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		memcpy((char *)&saddr->sin_addr, hp->h_addr, 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	int prognum, vers, port;
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	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_int, &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 */
Xunsigned int size;
X{
X	register char *mem;
X	char *malloc();
X	
X	if ((mem = (char *)malloc(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)
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 (Generic File System)
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 13093 -ne `wc -c <'cknfs.c'`; then
    echo shar: \"'cknfs.c'\" unpacked with wrong size!
fi
# end of 'cknfs.c'
fi
echo shar: End of shell archive.
exit 0