home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / sun / volume1 / cknfs / cknfs.c
Encoding:
C/C++ Source or Header  |  1989-06-21  |  12.8 KB  |  580 lines

  1. /*
  2.  * cknfs - Check for dead NFS servers
  3.  *
  4.  * Don't you hate it when you login to an NFS client, only to find
  5.  * yourself hung because one of your paths points to a dead NFS server?
  6.  * Well, this program fixes that problem.  It takes a list of execution
  7.  * paths as arguments.   Each path is examined for an NFS mount point.
  8.  * If found, the corresponding NFS server is checked.   Paths that lead
  9.  * to dead NFS servers are ignored.  The remaining paths are printed to
  10.  * stdout.  No more hung logins!
  11.  *
  12.  * Usage: cknfs -e -s -t# -v -D -L paths
  13.  *  
  14.  *       -e     silent, do not print paths
  15.  *       -s     print paths in sh format (colons)
  16.  *       -t n   timeout interval before assuming an NFS
  17.  *              server is dead (default 10 seconds)
  18.  *       -v     verbose
  19.  *       -D     debug
  20.  *       -L     expand symbolic links
  21.  *
  22.  * Typical examples:
  23.  *
  24.  *    set path = `cknfs /bin /usr/bin /usr/ucb . /usr6/bin /sdg/bin`
  25.  *    alias cd 'cknfs -e \!*; if ($status == 0) chdir \!*'
  26.  *
  27.  * The latter example prevents you from hanging if you cd to a
  28.  * directory that leads to a dead NFS server.
  29.  *
  30.  * Adminstrative note:  You can still get hung if your administator 
  31.  * mixes NFS mount points from different machines in the same parent
  32.  * directory or if your administrator mixes regular directories and
  33.  * NFS mount points in the same parent directory.
  34.  *
  35.  * The best organization is an overall /nfs directory with subdirectories 
  36.  * for each machine.   For example, if you have 3 NFS servers named
  37.  * "newton", "bardeen", and "zaphod", a good organization would be
  38.  *
  39.  *    /nfs/bardeen/apps
  40.  *    /nfs/bardeen/bin
  41.  *    /nfs/newton/bin
  42.  *    /nfs/newton/local
  43.  *    /nfs/zaphod/bin
  44.  *    /nfs/zaphod/sdg
  45.  *
  46.  * NEVER MIX MOUNT POINTS FROM DIFFERENT MACHINES IN THE SAME
  47.  * PARENT DIRECTORY!  Follow this rule and use this program and
  48.  * you will never get hung by NFS again.
  49.  */
  50.  
  51. /*
  52.  * Copyright (c) 1988, The Board of Trustees of the University of Illinois
  53.  * National Center for Supercomputing Applications.
  54.  *
  55.  * No warranty is expressed or implied.
  56.  * Unlimited redistribution permitted.
  57.  *
  58.  */
  59.  
  60. static char *RCSid = "$Header: cknfs.c,v 1.4 89/05/31 18:24:49 aklietz Exp $";
  61.  
  62. /*
  63.  * $Log:    cknfs.c,v $
  64.  * Revision 1.4  89/05/31  18:24:49  aklietz
  65.  * Fix bug introduced in rev 1.3 that did hangable lstat before
  66.  * checking for NFS mount point.
  67.  * Add support for Ultrix.
  68.  * 
  69.  * Revision 1.3  89/05/29  03:30:55  aklietz
  70.  * Terminate silently if no args in -e mode.
  71.  * Fix omission of chdir("/") during parse of symlink to absolute path.
  72.  * 
  73.  * Revision 1.2  89/05/26  14:14:35  aklietz
  74.  * Baseline for release
  75.  * 
  76.  * Revision 1.1  89/05/26  13:37:39  aklietz
  77.  * Initial revision
  78.  * 
  79.  */
  80.  
  81. #include <sys/param.h>
  82. #include <errno.h>
  83. #include <sys/types.h>
  84. #include <sys/stat.h>
  85. #include <signal.h>
  86. #include <ctype.h>
  87. #include <stdio.h>
  88. #include <sys/time.h>
  89. #include <sys/socket.h>
  90. #include <arpa/inet.h>
  91. #include <netdb.h>
  92. #include <rpc/rpc.h>
  93. #include <rpc/pmap_prot.h>
  94. #include <rpc/pmap_clnt.h>
  95. #include <nfs/nfs.h>
  96.  
  97. /*
  98.  * Make initial program
  99.  * May 1989, Alan Klietz (aklietz@ncsa.uiuc.edu)
  100.  */
  101.  
  102. #define DEFAULT_TIMEOUT 10  /* Default timeout for checking NFS server */
  103.  
  104. extern char *realloc();
  105. extern char *strchr(), *strrchr(), *strtok();
  106.  
  107. struct m_mlist {
  108.     int mlist_checked; /* -1 if bad, 0 if not checked, 1 if ok */
  109.     struct m_mlist *mlist_next;
  110.     char *mlist_dir;
  111.     char *mlist_fsname;
  112.     int mlist_isnfs;
  113. };
  114. static struct m_mlist *firstmnt;
  115.  
  116. static int errflg;
  117. static int eflg, sflg, vflg, Dflg, Lflg;
  118. static int timeout = DEFAULT_TIMEOUT;
  119. static char prefix[MAXPATHLEN];
  120. struct m_mlist *isnfsmnt();
  121. char *xalloc();
  122. void mkm_mlist();
  123.  
  124. int
  125. main(argc, argv)
  126. int argc;
  127. char **argv;
  128. {
  129.     register int n;
  130.     register char *s;
  131.     int good = 0;
  132.     char outbuf[BUFSIZ];
  133.     char errbuf[BUFSIZ];
  134.     extern int optind;
  135.     extern char *optarg;
  136.  
  137.  
  138.     /*
  139.      * Avoid intermixing stdout and stderr
  140.      */
  141.     setvbuf(stdout, outbuf, _IOFBF, sizeof(outbuf));
  142.     setvbuf(stderr, errbuf, _IOLBF, sizeof(errbuf));
  143.  
  144.     while ((n = getopt(argc, argv, "est:vDL")) != EOF)
  145.         switch(n) {
  146.             case 'e':    ++eflg;
  147.                     break;
  148.  
  149.             case 's':    ++sflg;
  150.                     break;
  151.  
  152.             case 't':    timeout = atoi(optarg);
  153.                     break;
  154.  
  155.             case 'v':    ++vflg;
  156.                     break;
  157.  
  158.             case 'D':    ++Dflg; ++vflg;
  159.                     break;
  160.  
  161.             case 'L':    ++Lflg;
  162.                     break;
  163.  
  164.             default:
  165.                     ++errflg;
  166.         }
  167.  
  168.     if (argc <= optind && !eflg) /* no paths */
  169.         ++errflg;
  170.  
  171.     if (errflg) {
  172.         fprintf(stderr, "Usage: %s -e -s -t# -v -D -L paths\n", argv[0]);
  173.         fprintf(stderr, "\tCheck paths for dead NFS servers\n");
  174.         fprintf(stderr, "\tGood paths are printed to stdout\n\n");
  175.         fprintf(stderr, "\t -e\tsilent, do not print paths\n");
  176.         fprintf(stderr, "\t -s\tprint paths in sh format (semicolons)\n");
  177.         fprintf(stderr, "\t -t n\ttimeout interval before assuming an NFS\n");
  178.         fprintf(stderr, "\t\tserver is dead (default 10 seconds)\n");
  179.         fprintf(stderr, "\t -v\tverbose\n");
  180.         fprintf(stderr, "\t -D\tdebug\n");
  181.         fprintf(stderr, "\t -L\texpand symbolic links\n\n");
  182.         exit(1);
  183.     }
  184.  
  185.     for (n = optind; n < argc; ++n) {
  186.         s = argv[n];
  187.         if (Dflg)
  188.             fprintf(stderr, "chkpath(%s)\n", s);
  189.         if (chkpath(s)) {
  190.             if (good++ && !eflg)
  191.                 putchar(sflg ? ':' : ' ');
  192.             if (!eflg)
  193.                 fputs(Lflg ? prefix : s, stdout);
  194.         } else
  195.             if (vflg)
  196.                 fprintf(stderr, "path skipped: %s\n",
  197.                     Lflg ? prefix : s);
  198.     }
  199.  
  200.     if (good && !eflg)
  201.         putchar('\n');
  202.  
  203.     fflush(stderr);
  204.     fflush(stdout);
  205.  
  206.     exit(good == 0 && optind < argc );
  207. }
  208.  
  209.  
  210. int chkpath(path)
  211. /*
  212.  * Check path for accessibility.  Return 1 if ok, 0 if error
  213.  */
  214. char *path;
  215. {
  216.     if (*path != '/') { /* If not absolute path, get initial prefix */
  217.         if (getwd(prefix) < 0) {
  218.             fprintf(stderr, "%s\n", prefix);
  219.             return 0;
  220.         }
  221.     }
  222.     return(_chkpath(path));
  223. }
  224.  
  225.  
  226. #define NTERMS 256
  227.  
  228. int
  229. _chkpath(path)
  230. char *path;
  231. {
  232.     register char *s, *s2;
  233.     register int i, front=0, back=0;
  234.     struct stat stb;
  235.     struct m_mlist *mlist;
  236.     char p[MAXPATHLEN];
  237.     char symlink[MAXPATHLEN];
  238.     char *queue[NTERMS];
  239.  
  240.  
  241.     /*
  242.      * Copy path to working storage
  243.      */
  244.     strncpy(p, path, sizeof(p)-1);
  245.  
  246.     if (*p == '/') { /* If absolute path, start at root */
  247.         *prefix = '\0';
  248.         (void) chdir("/");
  249.     }
  250.  
  251.     if (Dflg)
  252.         fprintf(stderr, "_chkpath(%s) prefix=%s\n", path, prefix);
  253.  
  254.     /*
  255.      * Put directory terms on FIFO queue
  256.      */
  257.     for (s = strtok(p, "/"); s != NULL; s = strtok(NULL, "/")) {
  258.         if (back >= NTERMS) {
  259.             fprintf(stderr, "Too many subdirs: %s\n", path);
  260.             goto fail;
  261.         }
  262.         queue[back++] = s;
  263.     }
  264.     /*  queue[front] = a, queue[front+1] = b, ... queue[back] = null */
  265.  
  266.     /*
  267.      * Scan queue of directory terms, expanding 
  268.      * symbolic links recursively.
  269.      */
  270.     while (front != back) {
  271.         s = queue[front++];
  272.         /* Dot */
  273.         if (s[0] == '.' && s[1] == '\0')
  274.             continue;
  275.         /* Dot Dot */
  276.         if (s[0] == '.' && s[1] == '.' && s[2] == '\0') {
  277.             if (chdir("..") < 0) {
  278.                 perror("chdir(..)");
  279.                 goto fail;
  280.             }
  281.             /* Remove trailing component of prefix */
  282.             if ((s2 = strrchr(prefix, '/')) != NULL)
  283.                 *s2 = '\0';
  284.             continue;
  285.         } else {
  286.             strcat(prefix, "/");
  287.             strcat(prefix, s);
  288.         }
  289.  
  290.         if ((mlist = isnfsmnt(prefix)) != NULL) /* NFS mount? */
  291.             if (chknfsmnt(mlist) <= 0)
  292.                 return 0;
  293.  
  294.         /* Check if symlink */
  295.         if (lstat(s, &stb) < 0) {
  296.             perror(s);
  297.             goto fail;
  298.         }
  299.         if ((stb.st_mode & S_IFMT) != S_IFLNK) {
  300.             /* not symlink */
  301.             if (chdir(s) < 0) {
  302.                 fprintf(stderr, "chdir(%s): ", s);
  303.                 perror("");
  304.                 goto fail;
  305.             }
  306.             continue;
  307.         }
  308.  
  309.         /* Remove symlink from tail of prefix */
  310.         if ((s2 = strrchr(prefix, '/')) != NULL)
  311.             *s2 = '\0';
  312.         /* 
  313.          * Read symlink
  314.          */
  315.         if ((i = readlink(s, symlink, MAXPATHLEN-1)) < 0) {
  316.             perror(s);
  317.             goto fail;
  318.         }
  319.         symlink[i] = '\0'; /* null terminate */
  320.  
  321.         /*
  322.          * Recursively check symlink
  323.          */
  324.         if (_chkpath(symlink) == 0)
  325.             return 0;
  326.     }
  327.  
  328.     return 1;
  329.  
  330. fail:
  331.     return 0;
  332. }
  333.     
  334.  
  335. struct m_mlist *
  336. isnfsmnt(path)
  337. /*
  338.  * Return 1 if path is NFS mount point
  339.  */
  340. char *path;
  341. {
  342.     register struct m_mlist *mlist;
  343.     static int init;
  344.  
  345.     if (init == 0) {
  346.         ++init;
  347.         mkm_mlist();
  348.     }
  349.  
  350.     for (mlist = firstmnt; mlist != NULL; mlist = mlist->mlist_next) {
  351.         if (mlist->mlist_isnfs == 0)
  352.             continue;
  353.         if (strcmp(mlist->mlist_dir, path) == 0)
  354.             return(mlist);
  355.     }
  356.     return NULL;
  357. }
  358.  
  359.  
  360. static int
  361. get_inaddr(saddr, host)
  362. /*
  363.  * Translate host name to Internet address.
  364.  * Return 1 if ok, 0 if error
  365.  */
  366. struct sockaddr_in *saddr;
  367. char *host;
  368. {
  369.     register struct hostent *hp;
  370.  
  371.     memset((char *)saddr, 0, sizeof(struct sockaddr_in));
  372.     saddr->sin_family = AF_INET;
  373.     if ((saddr->sin_addr.s_addr = inet_addr(host)) == -1) {
  374.         if ((hp = gethostbyname(host)) == NULL) {
  375.             fprintf(stderr, "%s: unknown host\n", host);
  376.             return 0;
  377.         }
  378.         memcpy((char *)&saddr->sin_addr, hp->h_addr, hp->h_length);
  379.     }
  380.     return 1;
  381. }
  382.  
  383.  
  384. int
  385. chknfsmnt(mlist)
  386. /*
  387.  * Ping the NFS server indicated by the given mnt entry
  388.  */
  389. register struct m_mlist *mlist;
  390. {
  391.     register char *s;
  392.     register struct m_mlist *mlist2;
  393.     CLIENT *client;
  394.     struct sockaddr_in saddr;
  395.     int sock, len;
  396.     struct timeval tottimeout;
  397.     struct timeval interval;
  398.     int prognum, vers, port;
  399.     struct pmap pmap;
  400.     enum clnt_stat rpc_stat;
  401.     static char p[MAXPATHLEN];
  402.  
  403.     if (Dflg)
  404.         fprintf(stderr, "chknfsmnt(%s)\n", mlist->mlist_fsname);
  405.  
  406.     if (mlist->mlist_checked) /* if already checked this mount point */
  407.         return (mlist->mlist_checked);
  408.  
  409.     /*
  410.      * Save path to working storage and strip colon
  411.      */
  412.     strncpy(p, mlist->mlist_fsname, sizeof(p)-1);
  413.     if ((s = strchr(p, ':')) != NULL)
  414.         *s = '\0';
  415.     len = strlen(p);
  416.  
  417.     /*
  418.      * See if remote host already checked via another mount point
  419.      */
  420.     for (mlist2 = firstmnt; mlist2 != NULL; mlist2 = mlist2->mlist_next)
  421.         if (strncmp(mlist2->mlist_fsname, p, len) == 0 
  422.                 && mlist2->mlist_checked)
  423.             return(mlist2->mlist_checked);
  424.  
  425.     mlist->mlist_checked = -1; /* set failed */
  426.     if (vflg)
  427.         fprintf(stderr, "Checking %s..\n", p);
  428.     interval.tv_sec = 2;  /* retry interval */
  429.     interval.tv_usec = 0;
  430.  
  431.     /*
  432.      * Parse internet address
  433.      */
  434.     if (get_inaddr(&saddr, p) == 0)
  435.         return 0;
  436.     /*
  437.      * Get socket to remote portmapper
  438.      */
  439.     saddr.sin_port = htons(PMAPPORT);
  440.     sock = RPC_ANYSOCK;
  441.     if ((client = clntudp_create(&saddr, PMAPPROG, PMAPVERS, interval, 
  442.             &sock)) == NULL) {
  443.         clnt_pcreateerror(p);
  444.         return 0;
  445.     }
  446.     /*
  447.      * Query portmapper for port # of NFS server
  448.      */
  449.     pmap.pm_prog = NFS_PROGRAM;
  450.     pmap.pm_vers = NFS_VERSION;
  451.     pmap.pm_prot = IPPROTO_UDP;
  452.     pmap.pm_port = 0;
  453.     tottimeout.tv_sec = timeout;  /* total timeout */
  454.     tottimeout.tv_usec = 0;
  455.     if ((rpc_stat = clnt_call(client, PMAPPROC_GETPORT, xdr_pmap, &pmap,
  456.             xdr_u_int, &port, tottimeout)) != RPC_SUCCESS) {
  457.         clnt_perror(client, p);
  458.         clnt_destroy(client);
  459.         return 0;
  460.     }
  461.     clnt_destroy(client);
  462.  
  463.     if (port == 0) {
  464.         fprintf(stderr, "%s: NFS server not registered\n", p);
  465.         return 0;
  466.     }
  467.     /*
  468.      * Get socket to NFS server
  469.      */
  470.     saddr.sin_port = htons(port);
  471.     sock = RPC_ANYSOCK;
  472.     if ((client = clntudp_create(&saddr, NFS_PROGRAM, NFS_VERSION,
  473.             interval, &sock)) == NULL) {
  474.         clnt_pcreateerror(p);
  475.         return 0;
  476.     }
  477.     /*
  478.      * Ping NFS server
  479.      */
  480.     tottimeout.tv_sec = timeout;
  481.     tottimeout.tv_usec = 0;
  482.     if ((rpc_stat = clnt_call(client, NULLPROC, xdr_void, (char *)NULL,
  483.             xdr_void, (char *)NULL, tottimeout)) != RPC_SUCCESS) {
  484.         clnt_perror(client, p);
  485.         clnt_destroy(client);
  486.         return 0;
  487.     }
  488.     clnt_destroy(client);
  489.     mlist->mlist_checked = 1; /* set success */
  490.     if (vflg)
  491.         fprintf(stderr, "%s ok\n", p);
  492.     return 1;
  493. }
  494.  
  495.  
  496. char *
  497. xalloc(size)
  498. /*
  499.  * Alloc memory with error checks
  500.  */
  501. unsigned int size;
  502. {
  503.     register char *mem;
  504.     char *malloc();
  505.     
  506.     if ((mem = (char *)malloc(size)) == NULL) {
  507.         (void) fprintf(stderr, "out of memory\n");
  508.         exit(1);
  509.     }
  510.     return(mem);
  511. }
  512.  
  513. /*
  514.  * Begin machine dependent code for mount table 
  515.  */
  516.  
  517. #if defined(sun)
  518. #include <mntent.h>
  519. void
  520. mkm_mlist()
  521. /*
  522.  * Build list of mnt entries - Sun version
  523.  */
  524. {
  525.     FILE *mounted;
  526.     struct m_mlist *mlist;
  527.     struct mntent *mnt;
  528.  
  529.     if ((mounted = setmntent(MOUNTED, "r"))== NULL) {
  530.         perror(MOUNTED);
  531.         exit(1);
  532.     }
  533.     while ((mnt = getmntent(mounted)) != NULL) {
  534.         mlist = (struct m_mlist *)xalloc(sizeof(*mlist));
  535.         mlist->mlist_next = firstmnt;
  536.         mlist->mlist_checked = 0;
  537.         mlist->mlist_dir = xalloc(strlen(mnt->mnt_dir)+1);
  538.         (void) strcpy(mlist->mlist_dir, mnt->mnt_dir);
  539.         mlist->mlist_fsname = xalloc(strlen(mnt->mnt_fsname)+1);
  540.         (void) strcpy(mlist->mlist_fsname, mnt->mnt_fsname);
  541.         mlist->mlist_isnfs = !strcmp(mnt->mnt_type, MNTTYPE_NFS);
  542.         firstmnt = mlist;
  543.     }
  544.     (void) endmntent(mounted);
  545. }
  546. #endif
  547.  
  548. #if defined(ultrix)
  549. #include <sys/fs_types.h>
  550. #include <sys/mount.h>
  551. void
  552. mkm_mlist()
  553. /*
  554.  * Build list of mnt entries - Ultrix version (Generic File System)
  555.  */
  556. {
  557.     struct m_mlist *mlist;
  558.     struct fs_data fs_data;
  559.     int start=0, len;
  560.  
  561.     while ((len = getmnt(&start, &fs_data, sizeof(fs_data), 
  562.             NOSTAT_MANY, NULL)) > 0) {
  563.         mlist = (struct m_mlist *)xalloc(sizeof(*mlist));
  564.         mlist->mlist_next = firstmnt;
  565.         mlist->mlist_checked = 0;
  566.         mlist->mlist_dir = xalloc(strlen(fs_data.fd_path)+1);
  567.         (void) strcpy(mlist->mlist_dir, fs_data.fd_path);
  568.         mlist->mlist_fsname = 
  569.             xalloc(strlen(fs_data.fd_devname)+1);
  570.         (void) strcpy(mlist->mlist_fsname, fs_data.fd_devname);
  571.         mlist->mlist_isnfs = (fs_data.fd_fstype == GT_NFS);
  572.         firstmnt = mlist;
  573.     }
  574.     if (len < 0) {
  575.         perror("getmnt");
  576.         exit(1);
  577.     }
  578. }
  579. #endif
  580.