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