home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume21 / amd / part08 / srvr_nfs.c < prev   
Encoding:
C/C++ Source or Header  |  1990-04-10  |  11.6 KB  |  530 lines

  1. /*
  2.  * $Id: srvr_nfs.c,v 5.1.1.2 90/01/11 17:21:08 jsp Exp Locker: jsp $
  3.  *
  4.  * Copyright (c) 1990 Jan-Simon Pendry
  5.  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
  6.  * Copyright (c) 1990 The Regents of the University of California.
  7.  * All rights reserved.
  8.  *
  9.  * This code is derived from software contributed to Berkeley by
  10.  * Jan-Simon Pendry at Imperial College, London.
  11.  *
  12.  * Redistribution and use in source and binary forms are permitted
  13.  * provided that the above copyright notice and this paragraph are
  14.  * duplicated in all such forms and that any documentation,
  15.  * advertising materials, and other materials related to such
  16.  * distribution and use acknowledge that the software was developed
  17.  * by Imperial College of Science, Technology and Medicine, London, UK.
  18.  * The names of the College and University may not be used to endorse
  19.  * or promote products derived from this software without specific
  20.  * prior written permission.
  21.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  22.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  23.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  24.  *
  25.  *    %W% (Berkeley) %G%
  26.  */
  27.  
  28. /*
  29.  * NFS server modeling
  30.  */
  31.  
  32. #include "am.h"
  33. #include <netdb.h>
  34. #include <rpc/pmap_prot.h>
  35. #include "mount.h"
  36.  
  37. extern qelem nfs_srvr_list;
  38. qelem nfs_srvr_list = { &nfs_srvr_list, &nfs_srvr_list };
  39.  
  40. typedef struct nfs_private {
  41.     u_short np_mountd;    /* Mount daemon port number */
  42.     int np_ping;        /* Number of failed ping attempts */
  43.     int np_xid;        /* RPC transaction id for pings */
  44.     int np_error;        /* Error during portmap request */
  45. } nfs_private;
  46.  
  47. static int np_xid;    /* For NFS pings */
  48. #define    NPXID_ALLOC()    (++np_xid)
  49. /*#define    NPXID_ALLOC()    ((++np_xid&0x0fffffff) == 0 ? npxid_gc() : np_xid)*/
  50.  
  51. /*
  52.  * Number of pings allowed to fail before host is declared down
  53.  * - three-fifths of the allowed mount time...
  54.  */
  55. #define    MAX_ALLOWED_PINGS    ((((ALLOWED_MOUNT_TIME + 5 * AM_PINGER - 1) * 3) / 5) / AM_PINGER)
  56. /*
  57.  * How often to ping when starting a new server
  58.  */
  59. #define    FAST_NFS_PING        3
  60.  
  61. static int ping_len;
  62. static char ping_buf[sizeof(struct rpc_msg) + 32];
  63.  
  64. /*
  65.  * Startup the NFS ping
  66.  */
  67. static void start_ping()
  68. {
  69.     XDR ping_xdr;
  70.     struct rpc_msg ping_msg;
  71.  
  72.     rpc_msg_init(&ping_msg, NFS_PROGRAM, NFS_VERSION, NFSPROC_NULL);
  73.  
  74.     /*
  75.      * Create an XDR endpoint
  76.      */
  77.     xdrmem_create(&ping_xdr, ping_buf, sizeof(ping_buf), XDR_ENCODE);
  78.  
  79.     /*
  80.      * Create the NFS ping message
  81.      */
  82.     if (!xdr_callmsg(&ping_xdr, &ping_msg)) {
  83.         plog(XLOG_ERROR, "Couldn't create ping RPC message");
  84.         going_down(3);
  85.     }
  86.  
  87.     /*
  88.      * Find out how long it is
  89.      */
  90.     ping_len = xdr_getpos(&ping_xdr);
  91.  
  92.     /*
  93.      * Destroy the XDR endpoint - we don't need it anymore
  94.      */
  95.     xdr_destroy(&ping_xdr);
  96. }
  97.  
  98.  
  99. /*
  100.  * Called when a portmap reply arrives
  101.  */
  102. static void got_portmap(pkt, len, sa, ia, idv, done)
  103. voidp pkt;
  104. int len;
  105. struct sockaddr_in *sa, *ia;
  106. voidp idv;
  107. int done;
  108. {
  109.     fserver *fs2 = (fserver *) idv;
  110.     fserver *fs = 0;
  111.     ITER(fs, fserver, &nfs_srvr_list)
  112.         if (fs == fs2)
  113.             break;
  114.  
  115.     if (fs == fs2) {
  116.         u_long port = 0;    /* XXX - should be short but protocol is naff */
  117.         int error = done ? pickup_rpc_reply(pkt, len, (voidp) &port, xdr_u_long) : -1;
  118.         nfs_private *np = (nfs_private *) fs->fs_private;
  119.         if (!error && port) {
  120. #ifdef DEBUG
  121.             dlog("got port (%d) for mountd on %s", port, fs->fs_host);
  122. #endif
  123.             /*
  124.              * Grab the port number.  Portmap sends back
  125.              * an unsigned long in native ordering, so it
  126.              * needs converting to a unsigned short in
  127.              * network ordering.
  128.              */
  129.             np->np_mountd = htons((u_short) port);
  130.             np->np_error = 0;
  131.         } else {
  132. #ifdef DEBUG
  133.             dlog("Error fetching port for mountd on %s", fs->fs_host);
  134. #endif
  135.             /*
  136.              * Almost certainly no mountd running on remote host
  137.              */
  138.             np->np_error = error ? error : ETIMEDOUT;
  139.         }
  140.         if (fs->fs_flags & FSF_WANT)
  141.             wakeup_srvr(fs);
  142.     } else if (done) {
  143. #ifdef DEBUG
  144.         dlog("Got portmap for old port request");
  145. #endif
  146.     } else {
  147. #ifdef DEBUG
  148.         dlog("portmap request timed out");
  149. #endif
  150.     }
  151. }
  152.  
  153. /*
  154.  * Obtain portmap information
  155.  */
  156. static int call_portmap(fs, auth, prog, vers, prot)
  157. fserver *fs;
  158. AUTH *auth;
  159. unsigned long prog, vers, prot;
  160. {
  161.     struct rpc_msg pmap_msg;
  162.     int len;
  163.     char iobuf[UDPMSGSIZE];
  164.     int error;
  165.     struct pmap pmap;
  166.  
  167.     rpc_msg_init(&pmap_msg, PMAPPROG, PMAPVERS, (unsigned long) 0);
  168.     pmap.pm_prog = prog;
  169.     pmap.pm_vers = vers;
  170.     pmap.pm_prot = prot;
  171.     pmap.pm_port = 0;
  172.     len = make_rpc_packet(iobuf, sizeof(iobuf), PMAPPROC_GETPORT,
  173.             &pmap_msg, (voidp) &pmap, xdr_pmap, auth);
  174.     if (len > 0) {
  175.         struct sockaddr_in sin;
  176.         bzero((voidp) &sin, sizeof(sin));
  177.         sin = *fs->fs_ip;
  178.         sin.sin_port = htons(PMAPPORT);
  179.         error = fwd_packet(RPC_XID_PORTMAP, (voidp) iobuf, len,
  180.                 &sin, &sin, (voidp) fs, got_portmap);
  181.     } else {
  182.         error = -len;
  183.     }
  184.     return error;
  185. }
  186.  
  187. static void nfs_keepalive P((fserver*));
  188.  
  189. /*
  190.  * This is called when we get a reply to an RPC ping.
  191.  * The value of id wass taken from the nfs_private
  192.  * structure when the ping was transmitted.
  193.  */
  194. /*ARGSUSED*/
  195. static void nfs_pinged(pkt, len, sp, tsp, idv, done)
  196. voidp pkt;
  197. int len;
  198. struct sockaddr_in *sp, *tsp;
  199. voidp idv;
  200. int done;
  201. {
  202.     int xid = (int) idv;
  203.     fserver *fs;
  204.     int found_map = 0;
  205.  
  206.     if (!done)
  207.         return;
  208.  
  209.     /*
  210.      * For each node...
  211.      */
  212.     ITER(fs, fserver, &nfs_srvr_list) {
  213.         nfs_private *np = (nfs_private *) fs->fs_private;
  214.         if (np->np_xid == xid) {
  215.             /*
  216.              * Reset the ping counter.
  217.              * Update the keepalive timer.
  218.              * Log what happened.
  219.              */
  220.             if (fs->fs_flags & FSF_DOWN) {
  221.                 fs->fs_flags &= ~FSF_DOWN;
  222.                 if (fs->fs_flags & FSF_VALID) {
  223.                     plog(XLOG_INFO, "file server %s type nfs is up", fs->fs_host);
  224.                 } else {
  225.                     plog(XLOG_INFO, "file server %s type nfs starts up", fs->fs_host);
  226.                     fs->fs_flags |= FSF_VALID;
  227.                 }
  228.                 /*if (fs->fs_flags & FSF_WANT)
  229.                     wakeup_srvr(fs);*/
  230.             } else {
  231. #ifdef DEBUG
  232.                 dlog("file server %s type nfs is still up", fs->fs_host);
  233. #endif
  234.             }
  235.  
  236.             /*
  237.              * Speed up the pings again
  238.              */
  239.             if (np->np_ping >= MAX_ALLOWED_PINGS) {
  240.                 untimeout(fs->fs_cid);
  241.                 fs->fs_cid = timeout(fs->fs_pinger, nfs_keepalive, (voidp) fs);
  242.             }
  243.  
  244.             /*
  245.              * New RPC xid...
  246.              */
  247.             np->np_xid = NPXID_ALLOC();
  248.  
  249.             /*
  250.              * Failed pings is zero...
  251.              */
  252.             np->np_ping = 0;
  253.  
  254.             /*
  255.              * Recompute portmap information if not known
  256.              */
  257.             if (np->np_error < 0) {
  258.                 if (!nfs_auth)
  259.                     nfs_auth = authunix_create_default();
  260.                 if (!nfs_auth)
  261.                     np->np_error = ENOBUFS;
  262.                 else
  263.                     call_portmap(fs, nfs_auth, MOUNTPROG,
  264.                         MOUNTVERS, (unsigned long) IPPROTO_UDP);
  265.             }
  266.             found_map++;
  267.             break;
  268.         }
  269.     }
  270.  
  271. #ifdef DEBUG
  272.     if (found_map == 0)
  273.         dlog("Spurious ping packet");
  274. #endif
  275. }
  276.  
  277.  
  278. /*
  279.  * Keep track of whether a server is alive
  280.  */
  281. static void nfs_keepalive(fs)
  282. fserver *fs;
  283. {
  284.     int error;
  285.     nfs_private *np = (nfs_private *) fs->fs_private;
  286.     int fstimeo;
  287.  
  288.     /*
  289.      * Send an NFS ping to this node
  290.      */
  291.  
  292.     if (ping_len == 0)
  293.         start_ping();
  294.  
  295.     /*
  296.      * Queue the packet...
  297.      */
  298.     error = fwd_packet(MK_RPC_XID(RPC_XID_NFSPING, np->np_xid), (voidp) ping_buf,
  299.         ping_len, fs->fs_ip, (struct sockaddr_in *) 0, (voidp) np->np_xid, nfs_pinged);
  300.  
  301.     /*
  302.      * See if a hard error occured
  303.      */
  304.     switch (error) {
  305.     case ENETDOWN:
  306.     case ENETUNREACH:
  307.     case EHOSTDOWN:
  308.     case EHOSTUNREACH:
  309.         np->np_ping = MAX_ALLOWED_PINGS;    /* immediately down */
  310.         break;
  311.  
  312.     case 0:
  313. #ifdef DEBUG
  314.         dlog("Sent NFS ping to %s", fs->fs_host);
  315. #endif
  316.         break;
  317.     }
  318.  
  319.     /*
  320.      * If N pings have failed then guess that it is dead
  321.      */
  322.     if (np->np_ping >= MAX_ALLOWED_PINGS) {
  323.         if (!(fs->fs_flags & FSF_VALID)) {
  324.             /*
  325.              * Starts off down
  326.              */
  327.             plog(XLOG_INFO, "file server %s type nfs starts down", fs->fs_host);
  328.             fs->fs_flags |= FSF_VALID;
  329.             if (fs->fs_flags & FSF_WANT)
  330.                 wakeup_srvr(fs);
  331.         }
  332.  
  333.         if ((fs->fs_flags & FSF_DOWN) == 0) {
  334.             /*
  335.              * Server was up, but is now down.
  336.              */
  337.             plog(XLOG_INFO, "file server %s type nfs is down", fs->fs_host);
  338.             fs->fs_flags |= FSF_DOWN;
  339.             if (fs->fs_flags & FSF_WANT)
  340.                 wakeup_srvr(fs);
  341.             /*
  342.              * Since the server is down, the portmap
  343.              * information may now be wrong, so it
  344.              * must be flushed from the local cache
  345.              */
  346.             flush_fhandle_cache(fs);
  347.             np->np_error = -1;
  348.             np->np_ping = 1;
  349.         }
  350.     } else {
  351.         np->np_ping++;
  352. #ifdef DEBUG
  353.         if (np->np_ping > 1)
  354.             dlog("%d pings to %s failed - max %d allowed", np->np_ping, fs->fs_host, MAX_ALLOWED_PINGS);
  355. #endif
  356.     }
  357.  
  358.     /*
  359.      * Back off the ping interval if we are not getting replies and
  360.      * the remote system is know to be down.
  361.      */
  362.     switch (fs->fs_flags & (FSF_DOWN|FSF_VALID)) {
  363.     case FSF_VALID:            /* Up */
  364.         fstimeo = fs->fs_pinger;
  365.         break;
  366.     case FSF_VALID|FSF_DOWN:    /* Down */
  367.         fstimeo = np->np_ping * fs->fs_pinger;
  368.         break;
  369.     default:            /* Unknown */
  370.         fstimeo = FAST_NFS_PING;
  371.         break;
  372.     }
  373.     fs->fs_cid = timeout(fstimeo, nfs_keepalive, (voidp) fs);
  374. }
  375.  
  376. int nfs_srvr_port(fs, port, wchan)
  377. fserver *fs;
  378. u_short *port;
  379. voidp wchan;
  380. {
  381.     int error = -1;
  382.     if ((fs->fs_flags & FSF_VALID) == FSF_VALID) {
  383.         if ((fs->fs_flags & FSF_DOWN) == 0) {
  384.             nfs_private *np = (nfs_private *) fs->fs_private;
  385.             if (np->np_error == 0) {
  386.                 *port = np->np_mountd;
  387.                 /*
  388.                  * Now go get it again in case it changed
  389.                  */
  390.                 np->np_error = -1;
  391.                 error = 0;
  392.             } else {
  393.                 error = np->np_error;
  394.             }
  395.         } else {
  396.             error = EWOULDBLOCK;
  397.         }
  398.     }
  399.     if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) {
  400.         /*
  401.          * If a wait channel is supplied, and no
  402.          * error has yet occured, then arrange
  403.          * that a wakeup is done on the wait channel,
  404.          * whenever a wakeup is done on this fs node.
  405.          * Wakeup's are done on the fs node whenever
  406.          * it changes state - thus causing control to
  407.          * come back here and new, better things to happen.
  408.          */
  409.         fs->fs_flags |= FSF_WANT;
  410.         sched_task(wakeup_task, wchan, (voidp) fs);
  411.     }
  412.     return error;
  413. }
  414.  
  415. static void start_nfs_pings(fs)
  416. fserver *fs;
  417. {
  418.     if (!(fs->fs_flags & FSF_PINGING)) {
  419.         fs->fs_flags |= FSF_PINGING;
  420.         if (fs->fs_cid)
  421.             untimeout(fs->fs_cid);
  422.         nfs_keepalive(fs);
  423.     } else {
  424. #ifdef DEBUG
  425.         dlog("Already running pings to %s", fs->fs_host);
  426. #endif
  427.     }
  428. }
  429.  
  430. /*
  431.  * Find an nfs server for a host.
  432.  */
  433. fserver *find_nfs_srvr(mf)
  434. mntfs *mf;
  435. {
  436.     fserver *fs;
  437.     struct hostent *hp = 0;
  438.     char *host = mf->mf_fo->opt_rhost;
  439.     struct sockaddr_in *ip;
  440.     nfs_private *np;
  441.  
  442. top:
  443.     /*
  444.      * Scan the list of known servers looking
  445.      * for one with the same name
  446.      */
  447.     ITER(fs, fserver, &nfs_srvr_list) {
  448.         if (STREQ(host, fs->fs_host)) {
  449.             start_nfs_pings(fs);
  450.             fs->fs_refc++;
  451.             return fs;
  452.         }
  453.     }
  454.  
  455.     /*
  456.      * If the name is not known, it may be
  457.      * because it was an alternate name for
  458.      * the same machine.  So do a lookup and
  459.      * try again with the primary name if that
  460.      * is different.
  461.      * All that assuming it wasn't normalized
  462.      * earlier of course...
  463.      */
  464.     if (hp == 0) {
  465.         hp = gethostbyname(host);
  466.         if (hp && !STREQ(host, hp->h_name) && !normalize_hosts) {
  467.             host = hp->h_name;
  468.             goto top;
  469.         }
  470.     }
  471.  
  472.     /*
  473.      * Get here if we can't find an entry
  474.      */
  475.     if (hp) {
  476.         switch (hp->h_addrtype) {
  477.         case AF_INET:
  478.             ip = ALLOC(sockaddr_in);
  479.             bzero((voidp) ip, sizeof(*ip));
  480.             ip->sin_family = AF_INET;
  481.             ip->sin_addr = *(struct in_addr *) hp->h_addr;
  482.             ip->sin_port = htons(NFS_PORT);
  483.             break;
  484.  
  485.         default:
  486.             ip = 0;
  487.             break;
  488.         }
  489.     } else {
  490.         ip = 0;
  491.     }
  492.  
  493.     /*
  494.      * Allocate a new server
  495.      */
  496.     fs = ALLOC(fserver);
  497.     fs->fs_refc = 1;
  498.     fs->fs_host = strdup(hp ? hp->h_name : "unknown_hostname");
  499.     fs->fs_ip = ip;
  500.     fs->fs_cid = 0;
  501.     if (ip) {
  502.         fs->fs_flags = FSF_DOWN;    /* Starts off down */
  503.     } else {
  504.         fs->fs_flags = FSF_ERROR|FSF_VALID;
  505.         mf->mf_flags |= MFF_ERROR;
  506.         mf->mf_error = ENOENT;
  507.     }
  508.     fs->fs_pinger = AM_PINGER;
  509.     np = ALLOC(nfs_private);
  510.     np->np_xid = NPXID_ALLOC();
  511.     bzero((voidp) np, sizeof(*np));
  512.     np->np_error = -1;
  513.     fs->fs_private = (voidp) np;
  514.     fs->fs_prfree = free;
  515.  
  516.     if (!(fs->fs_flags & FSF_ERROR)) {
  517.         /*
  518.          * Start of keepalive timer
  519.          */
  520.         start_nfs_pings(fs);
  521.     }
  522.  
  523.     /*
  524.      * Add to list of servers
  525.      */
  526.     ins_que(&fs->fs_q, &nfs_srvr_list);
  527.  
  528.     return fs;
  529. }
  530.