v01i033: Cknfs - Test NFS paths for validity
Charles Mcgrew
mcgrew at dartagnan.rutgers.edu
Thu Jun 22 03:31:18 AEST 1989
Submitted-by: aklietz at 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 at 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 at 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
More information about the Comp.sources.sun
mailing list