[comp.protocols.appletalk] kboot part 1/3

bobs@randvax.UUCP (Rober Schwartzkopf) (01/30/91)

Due to the large number of people interested in kboot I've decided to
post it here.

Bob Schwartzkopf (bobs@rand.org)

#! /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 archive 1 (of 3)."
# Contents:  aarpd.c cmd.c getaa.c icmp.c kboot.c nitlib.c
# Wrapped by bobs@chumley on Tue Jan 29 17:41:51 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'aarpd.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'aarpd.c'\"
else
echo shar: Extracting \"'aarpd.c'\" \(7348 characters\)
sed "s/^X//" >'aarpd.c' <<'END_OF_FILE'
X#ifndef lint
Xstatic char	*RCSid="$Header: /tmp_mnt/home/src/rand/etc/kboot/RCS/aarpd.c,v 1.1 91/01/29 17:36:55 root Exp $";
X#endif lint
X
X/*
X * $Log:	aarpd.c,v $
X * Revision 1.1  91/01/29  17:36:55  root
X * Initial revision
X * 
X */
X
X/*
X * aarpd - Appletalk ARP daemon.  Obtain an appletalk protocol address,
X *	   then listen forever, responding to any appletalk probes and
X *	   requests sent to us.  Depends on SunOS 4.x NIT interface.
X *
X * Author: Bob Schwartzkopf, The RAND Corporation.  Based on an earlier
X *	   version written by Dan Tappan at BBN that ran under SunOS 3.x.
X *
X * Comments, suggestions, patches, bug reports, etc. may be sent to
X * bobs@rand.org.
X */
X
X#include <stdio.h>
X#include <syslog.h>
X#include <sys/types.h>
X#include <sys/errno.h>
X#include <sys/socket.h>
X#include <sys/fcntl.h>
X#include <sys/termios.h>
X#include <net/if.h>
X#include <netinet/in.h>
X#include <netinet/if_ether.h>
X#include <rpc/rpc.h>
X#include <sys/time.h>
X#include <sys/timeb.h>
X#include "appletalk.h"
X#include "aarpd.h"
X#include "config.h"
X#include "patchlevel.h"
X
Xextern char	*rindex ();
Xint		aarp_read ();
X
Xextern struct ether_addr	myether;	/* Setup by nit_init	*/
Xextern int			errno;
Xextern char			*sys_errlist[];
X
X#define VERSION		2
X
X/*
X * Globals.
X */
Xchar			*progname;
Xchar			*interface = NULL;
Xstruct ether_addr	ebc = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
Xlong			atalkaddr;	/* Current appletalk address	*/
Xint			haveaddr = 0;	/* Have appletalk address	*/
Xint			conflict;	/* We're in confict with someone*/
Xint			detached = 0;	/* Detached from tty		*/
Xint			debug = 0;	/* Debugging			*/
Xstruct ether_header	eh;		/* Output ethernet header	*/
Xstruct ether_aarp	ap;		/* Output packet		*/
X
Xmain (argc, argv)
X
Xint	argc;
Xchar	**argv;
X
X{
X  char	c;
X  int	i;
X  
X  if ((progname = rindex (argv[0], '/')) == NULL)
X      progname = *argv;
X  else
X      progname++;
X  while (--argc > 0) {
X      argv++;
X      c = (*argv)[0] == '-' ? (*argv)[1] : (*argv)[0];
X      switch (c) {
X	case 'd' :
X	  debug++;
X	  break;
X	case 'i' :
X	  if (argc <= 1)
X	      usage ();
X	  argv++;
X	  argc--;
X	  interface = *argv;
X	  break;
X	case 'v' :
X	  printf ("%s version number %d.%d\n", progname, VERSION, PATCHLEVEL);
X	  exit (0);
X	default :
X	  usage ();
X      }
X  }
X  if (!debug) {				/* Detach ourselves		*/
X      detached++;
X      if (fork ())
X	  exit (0);
X      if ((i = open ("/dev/tty", O_RDONLY)) >= 0)
X	  ioctl (i, TIOCNOTTY, 0);
X      i = getdtablesize ();
X      while (--i >= 0)
X	  close (i);
X      open ("/dev/null", 0);
X      dup2 (0, 1);
X      dup2 (0, 2);
X      openlog (progname, 0, LOG_DAEMON);
X  }
X  nit_init (interface);
X  nit_open (sizeof (ap) + sizeof (eh), AARP);
X  aarp_probe ();			/* Get appletalk address	*/
X  initrpc ();				/* Prepare for RPC		*/
X  ap.aarp_op = htons (AARPResp);	/* Set up for sending replies	*/
X  nit_dispatch (aarp_read, 0, &svc_fdset, svc_getreqset, NULL);
X}
X
X/*
X * usage - Print usage and die.
X */
Xusage ()
X
X{
X  fprintf (stderr, "Usage: %s [-d] [-i interface]\n", progname);
X  exit (1);
X}
X
X/*
X * aarp_probe - Get unused appletalk address.  Algorithm is to try each
X *   in turn, probing to see if anyone else is using each address.  When
X *   no one answers a probe, we use that address.  Note this routine
X *   initializes some global data structures (eh, ap), parts of which are
X *   used later by aarp_read ().
X */
Xaarp_probe ()
X
X{
X  u_long		spa;
X  int			i;
X  struct timeval	timeout;
X  struct timeb		tstart;
X  struct timeb		tcur;
X  int			utotal;
X  int			ucur;
X
X	/* Init ether header		*/
X  ether_copy (&ebc, &eh.ether_dhost);
X  ether_copy (&myether, &eh.ether_shost);
X  eh.ether_type = htons (AARP);
X	/* Init aarp header		*/
X  ap.aarp_hrd = htons (H_Ethernet);
X  ap.aarp_pro = htons (P_AppleTalk);
X  ap.aarp_hln = HL_Ethernet;
X  ap.aarp_pln = PL_AppleTalk;
X  ap.aarp_op = htons (AARPProbe);
X  ether_copy (&myether, &ap.aarp_sha);
X  bzero (&ap.aarp_tha, HL_Ethernet);
X  nit_timeout (APrbTicks, &timeout);
X  utotal = timeout.tv_sec * 1000000 + timeout.tv_usec;
X  for (atalkaddr = 1; atalkaddr < 254; atalkaddr++) {
X      spa = htonl (atalkaddr);
X      bcopy (&spa, ap.aarp_spa, PL_AppleTalk);
X      bcopy (&spa, ap.aarp_tpa, PL_AppleTalk);
X      conflict = 0;
X      for (i = 0; !conflict && i < APrbTries; i++) {
X	  nit_write ((caddr_t) &eh, (caddr_t) &ap, sizeof ap);
X	  ftime (&tstart);
X	  do {
X	      nit_dispatch (aarp_read, 1, NULL, NULL, &timeout);
X	      ftime (&tcur);
X	      ucur = (tcur.time - tstart.time) * 1000000 +
X		     (tcur.millitm - tstart.millitm) * 1000;
X	  } while (!conflict && utotal > ucur);
X      }
X      if (!conflict) {
X	  if (debug)
X	      fprintf (stderr, "aarp_probe: Using appletalk address %d\n",
X		       atalkaddr);
X	  break;
X      }
X  }
X  if (conflict) {
X      logerr ("aarp_probe: Couldn't find appletalk address, exiting...\n");
X      exit (1);
X  }
X  haveaddr++;
X}
X
X/*
X * aarp_read - Handle AARP response packets.
X */
Xaarp_read (p, pl)
X
Xstruct aarp_packet	*p;
Xint			pl;
X
X{
X  int	op;
X  long	spa;
X  long	tpa;
X
X  if (pl != sizeof (*p)) {
X      if (debug)
X          fprintf (stderr, "aarp_read: Truncated packet len %d\n", pl);
X      return;
X  }
X  op = ntohs (p->ap_op);
X  bcopy ((caddr_t) p->ap_spa, &spa, PL_AppleTalk);
X  spa = ntohl (spa);
X  switch (op) {
X    case AARPProbe :
X    case AARPReq :
X      bcopy ((caddr_t) p->ap_tpa, &tpa, PL_AppleTalk);
X      tpa = ntohl (tpa);
X      if (haveaddr && tpa == atalkaddr) {
X	/*
X	 * Most of the output header and packet (eh, ap) should already be
X	 * set up since it was used for probing initially.  Should only need
X	 * to fill in destination addresses.
X	 */
X	  ether_copy (&p->ap_sha, &eh.ether_dhost);
X	  ether_copy (&p->ap_sha, &ap.aarp_tha);
X	  bcopy (p->ap_spa, ap.aarp_tpa, PL_AppleTalk);
X	  if (debug) {
X	      fprintf (stderr, "aarp_read: Replying to %s from %d (%s)\n",
X		      op == AARPProbe ? "probe" : "request",
X		      spa, ether_ntoa (&p->ap_sha));
X	  }
X	  nit_write ((caddr_t) &eh, (caddr_t) &ap, sizeof ap);
X      }
X      break;
X
X    case AARPResp :
X      if (debug)
X	  fprintf (stderr, "aarp_read: Reply from %d (%s)\n",
X		   spa, ether_ntoa (&p->ap_sha));
X      if (!haveaddr && spa == atalkaddr)
X          conflict++;
X      break;
X
X    default :
X      logerr ("aarp_read: Unknown AARP operation %d\n", op);
X  }
X}
X
X/*
X * getaa - Return appletalk address for this host.
X */
Xgetaa (request, xprt)
X
Xstruct svc_req	*request;
XSVCXPRT		*xprt;
X
X{
X  struct sockaddr_in	*sa;
X
X  if (debug) {
X      sa = svc_getcaller (xprt);
X      fprintf (stderr, "getaa: Returning %d to %s\n",
X	      atalkaddr, inet_ntoa (sa->sin_addr));
X  }
X  switch (request->rq_proc) {
X    case NULLPROC:
X      if (!svc_sendreply (xprt, xdr_void, 0)) {
X	  logerr ("getaa(NULLPROC): svc_sendreply failed\n");
X	  exit (1);
X      }
X      break;
X
X    case GETAAPROC:
X      if (!svc_sendreply (xprt, xdr_u_long, (caddr_t) &atalkaddr)) {
X	  logerr ("getaa(GETAAPROC): svc_sendreply failed\n");
X	  exit (1);
X      }
X      break;
X
X    default:
X      svcerr_noproc (xprt);
X      break;
X  }
X}
X
X/*
X * Initialize RPC stuff.
X */
Xinitrpc ()
X
X{
X  SVCXPRT	*xprt;
X
X  if ((xprt = svcudp_create (RPC_ANYSOCK)) == NULL) {
X      logerr ("initrpc: svcudp_create failed\n");
X      exit (1);
X  }
X  pmap_unset (GETAAPROG, GETAAVERS);
X  if (!svc_register (xprt, GETAAPROG, GETAAVERS, getaa, IPPROTO_UDP)) {
X      logerr ("initrpc: Can't register %d %d\n", GETAAPROG, GETAAVERS);
X      exit (1);
X  }
X}
END_OF_FILE
if test 7348 -ne `wc -c <'aarpd.c'`; then
    echo shar: \"'aarpd.c'\" unpacked with wrong size!
fi
# end of 'aarpd.c'
fi
if test -f 'cmd.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cmd.c'\"
else
echo shar: Extracting \"'cmd.c'\" \(19050 characters\)
sed "s/^X//" >'cmd.c' <<'END_OF_FILE'
X#ifndef lint
Xstatic char	*RCSid="$Header: /tmp_mnt/home/src/rand/etc/kboot/RCS/cmd.c,v 1.1 91/01/29 17:36:59 root Exp $";
X#endif lint
X
X/*
X * $Log:	cmd.c,v $
X * Revision 1.1  91/01/29  17:36:59  root
X * Initial revision
X * 
X */
X
X/*
X * cmd.c - Send commands to kboxes.
X */
X
X#include <stdio.h>
X#include <syslog.h>
X#include <netdb.h>
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <sys/errno.h>
X#include <net/if.h>
X#include <netinet/in.h>
X#include <netinet/if_ether.h>
X#include <sys/time.h>
X#include <sys/timeb.h>
X#include "appletalk.h"
X#include "cmdidx.h"
X#include "cmdmacro.h"
X#include "kbox.h"
X#include "config.h"
X
Xextern struct hostent		*gethostbyaddr ();
Xextern char			*inet_ntoa ();
Xstruct kbox			*get_kbox ();
Xextern int			errno;
Xextern char			*sys_errlist[];
Xextern int			numkbox;
Xextern int			debug;
Xextern struct ether_addr	ebc;
Xextern struct ether_header	eh;
Xextern struct kbox		*kboxtab[HTSIZE];
Xextern long			atalkaddr;
Xextern unsigned char		kboxstate[STATE_LENGTH];
Xextern unsigned char		newstate[STATE_LENGTH];
Xextern char			*etcdir;
Xextern unsigned char		*zonelistp;
X
X#define HEX(i)			((i) < 10 ? '0' + (i) : 'A' + (i) - 10)
X
X/*
X * Globals.
X */
Xint			(*recv_copyproc) () = NULL;
Xstruct ether_fastpath	fppkt;
Xstruct fp_promram	promram;
Xint			num_resp;
Xint			expected_resp;
Xint			destkbox;
Xstruct kbox		*kboxaddr[MAXKBOX];
Xstruct timeval		timeout;
X
X/*
X * Receive functions.  There should be a recv_ function for each send_
X * function.
X */
X
X/*
X * recv_who - Process who packet.  Algorithm is to look up the ethernet
X *   address in the ethers file and then add this kbox to the hash
X *   table it's not already there.  It would probably be more efficient
X *   to look up the ethernet address directly in a table hashed by ethernet
X *   address, however recv_who isn't called very often so I won't bother.
X */
Xrecv_who (p)
X
Xstruct fp_packet	*p;
X
X{
X  char		name[MAXHOSTNAME];
X  char		buf[20];
X  struct kbox	*kp;
X
X  if (ether_ntohost (name, &p->fp_shost) != 0) {
X      logerr ("recv_who: Can't find %s in ethers table\n",
X	      ether_ntoa (&p->fp_shost));
X      return;
X  }
X  if (kp = get_kbox (name)) {
X      if (!bcmp (&kp->ea, &p->fp_shost, sizeof(struct ether_addr))) {
X	  if (kp->aa == AT_Broadcast) {
X	      kp->aa = p->fp_lapsrc;
X	      kboxaddr[kp->aa] = kp;
X	      if (debug)
X	          fprintf (stderr, "recv_who: Adding kbox %s %s\n",
X			   name, ether_ntoa (&p->fp_shost));
X	      num_resp++;
X	  }
X      } else {
X	  strcpy (buf, ether_ntoa (&kp->ea));
X	  logerr ("recv_who: Mismatched ether address for %s: %s != %s\n",
X		  name, buf, ether_ntoa (&p->fp_shost));
X      }
X  } else if (debug)
X      fprintf (stderr, "recv_who: Unknown kbox %s %s\n",
X	       name, ether_ntoa (&p->fp_shost));
X}
X
X/*
X * recv_promram - Receive promram vector.  On sparc machines the
X *   fp_promram structure doesn't align properly, so copy the count
X *   field separately.
X */
Xrecv_promram (p)
X
Xstruct fp_packet	*p;
X
X{
X  bcopy (p->fp_data, &promram.fpr_count, sizeof(promram.fpr_count));
X  bcopy (&p->fp_data[sizeof(promram.fpr_count)], &promram.fpr_jtable,
X	 PROMRAMSIZE - sizeof(promram.fpr_count));
X  num_resp++;
X}
X
X/*
X * recv_getstate - Receive state vector.
X */
Xrecv_getstate (p)
X
Xstruct fp_packet	*p;
X
X{
X  bcopy (&p->fp_data[FPCOPYSIZE], kboxstate, STATE_LENGTH);
X  if (debug) {
X      fprintf (stderr, "recv_getstate: Received state...\n");
X      show_state (stderr, kboxstate);
X  }
X  num_resp++;
X}
X
X/*
X * recv_rcsid - Receive rcs id string.
X */
Xrecv_rcsid (p)
X
Xstruct fp_packet	*p;
X
X{
X  char	rcsid[MAXFPPKT];
X
X  strcpy (rcsid, p->fp_data);
X  printf ("RCS ID: %s\n", rcsid);
X  num_resp++;
X}
X
X/*
X * recv_whereis - Receive whereis packet.
X */
Xrecv_whereis (p)
X
Xstruct fp_packet	*p;
X
X{
X  struct fp_whereis	w;
X
X  bcopy (p->fp_data, &w, sizeof(w));
X  printf ("Rom0: %x Rom1: %x Z8530: %x Port0: %x Port1: %x\n",
X	  w.fpw_rom0, w.fpw_rom1, w.fpw_8530, w.fpw_0port, w.fpw_1port);
X  printf ("Ramstart: %x Ramend: %x\n", w.fpw_ram2, w.fpw_ramtop);
X  num_resp++;
X}
X
X/*
X * recv_where2 - Receive where2 packet.
X */
Xrecv_where2 (p)
X
Xstruct fp_packet	*p;
X
X{
X  struct fp_where2	w2;
X  char			*m;
X
X  bcopy (p->fp_data, &w2.fpw2_model, sizeof(w2.fpw2_model));
X  bcopy (&p->fp_data[sizeof(w2.fpw2_model)], &w2.fpw2_enetprom,
X	 sizeof(w2) - sizeof(w2.fpw2_model));
X  m = (w2.fpw2_model == FPMODEL123) ? "FPMODEL123" :
X      (w2.fpw2_model == FPMODEL4) ? "FPMODEL4" : "UNKNOWN";
X  printf ("Model: %s (%d) Ethernet address ROM: %x\n",
X	  m, w2.fpw2_model, w2.fpw2_enetprom);
X  printf ("Extension RAM first word: %x last word: %x\n",
X	  w2.fpw2_exrambot, w2.fpw2_exramtop);
X  num_resp++;
X}
X
X/*
X * recv_state - Receive kbox state.
X */
Xrecv_state (p)
X
Xstruct fp_packet	*p;
X
X{
X  int	state;
X  char	*s;
X
X  bcopy (p->fp_data, &state, sizeof(state));
X  switch (state) {
X    case BOOTME			: s = "BOOTME"; break;
X    case DOWNLOAD_VALID		: s = "DOWNLOAD VALID"; break;
X    case FORCE_PROM_EXECUTION	: s = "FORCE PROM EXECUTION"; break;
X    case PROM1_VALID		: s = "PROM1 VALID"; break;
X    case BE_A_BRIDGE		: s = "BE A BRIDGE"; break;
X    case EXECUTING_BRIDGE	: s = "EXECUTING BRIDGE"; break;
X    case EXECUTING_DOWNLOAD	: s = "EXECUTING DOWNLOAD"; break;
X    case EXECUTING_PROM1	: s = "EXECUTING PROM1"; break;
X    default			: s = "UNKNOWN";
X  }
X  printf ("State: %s (%d)\n", s, state);
X  num_resp++;
X}
X
X/*
X * recv_version - Receive prom version.
X */
Xrecv_version (p)
X
Xstruct fp_packet	*p;
X
X{
X  struct fp_version	ver;
X  char			model[5];
X
X  bcopy (p->fp_data, &ver, sizeof(ver));
X  bcopy (&ver.fpv_model, model, sizeof(ver.fpv_model));
X  model[4] = '\0';
X  printf ("Jump table entries: %d Prom version: %d Model: %s\n",
X	  ver.fpv_entries, ver.fpv_version, model);
X  num_resp++;
X}
X
X/*
X * recv_hwstatus - Receive hardware status.
X */
Xrecv_hwstatus (p)
X
Xstruct fp_packet	*p;
X
X{
X  int	status;
X
X  bcopy (p->fp_data, &status, sizeof(status));
X  printf ("Hardware status:");
X  if (status) {      
X      if (status & SCC_BAD)
X	 printf (" SCC_BAD");
X      if (status & I82586_BAD)
X	  printf (" I82586_BAD");
X      if (status & BATTERY_LOW)
X	  printf (" BATTERY_LOW");
X      if (status & FUSE_BLOWN)
X	  printf (" FUSE_BLOWN");
X      if (status & RAMTEST_FAILED)
X	  printf (" RAMTEST_FAILED");
X  } else
X      printf (" OK");
X  printf (" (0x%x)\n", status);
X  num_resp++;
X}
X
X/*
X * inet_ntos - Translate ip address to hostname.
X */
Xchar *inet_ntos (addr)
X
Xunsigned char	*addr;
X
X{
X  struct in_addr	a;
X  struct hostent	*he;
X
X  bcopy (addr, &a.s_addr, 4);
X  if ((he = gethostbyaddr (&a, 4, AF_INET)) == NULL)
X      return (inet_ntoa (a));
X  else
X      return (he->h_name);
X}
X
X/*
X * show_state - Print state vector.
X */
Xshow_state (f, s)
X
XFILE		*f;
Xunsigned char	s[STATE_LENGTH];
X
X{
X  int	options;
X  int	i;
X  int	first;
X  long	l;
X  short	st;
X
X  fprintf (f, "\tAppletalk: %d.%d.%d Ethertalk: %d.%d.%d IPtalk: %d.%d.%d",
X	   s[O_ATNET], s[O_ATNET+1], s[O_NODE],
X	   s[O_ETNET], s[O_ETNET+1], s[O_ETNODE],
X	   s[O_UDPNET], s[O_UDPNET+1], s[O_UDPNODE]);
X  fprintf (f, " Bridge: %d\n", s[O_BRIDGE]);
X  fprintf (f, "\tEthernet: %s Name: \"%s\" File: \"%s\"\n",
X	   ether_ntoa (&s[O_ETHER]), &s[O_NAME], &s[O_FILE]);
X  fprintf (f, "\tConfig: \"%s\"\n", &s[O_CONFIG]);
X  fprintf (f, "\tATzone: \"%s\" ETzone: \"%s\" UDPzone: \"%s\"\n",
X	   &s[O_ATZONE], &s[O_ETZONE], &s[O_UDPZONE]);
X  fprintf (f, "\tPforce: %d Autoconfig: %s", s[O_PFORCE],
X	   (s[O_AUTOCONFIG] || s[O_AUTOCONFIG+1]) ? "yes" : "no");
X  fprintf (f, " Autoboot: %s",
X	   (s[O_AUTOBOOT] || s[O_AUTOBOOT+1]) ? "yes\n" : "no\n");
X  fprintf (f, "\tOptions: ");
X  bcopy (&s[O_OPTIONS], &options, sizeof(options));
X  if (options) {
X      for (first = 1, i = 0; i < 32; i++)
X	  if ((1 << i) & options)
X	      if (first) {
X		  first = 0;
X		  fprintf (f, "%d", i + 1);
X	      } else
X		  fprintf (f, ", %d", i+1);
X  } else
X      fprintf (f, "none");
X  bcopy (&s[O_SN], &l, sizeof (l));
X  bcopy (&s[O_TYPE], &st, sizeof (st));
X  fprintf (f, " Serial: %d Type: %d\n", l, st);
X  fprintf (f, "\tRouter: \"%s\"", inet_ntos (&s[O_IPDEFROUTER]));
X  fprintf (f, " ETip: \"%s\"", inet_ntos (&s[O_IPADDRESS]));
X  fprintf (f, " ATip: \"%s\"", inet_ntos (&s[O_ATIPADDRESS]));
X  fprintf (f, " Broadcast: \"%s\"\n", inet_ntos (&s[O_IPBROADCAST]));
X  fprintf (f, "\tSubnet: \"%s\"", inet_ntos (&s[O_IPSUBMASK]));
X  fprintf (f, " KIP: \"%s\"", inet_ntos (&s[O_IPKIPSERVER]));
X  fprintf (f, " Name: \"%s\"", inet_ntos (&s[O_NAMESERVER]));
X  fprintf (f, " File: \"%s\"\n", inet_ntos (&s[O_FILESERVER]));
X  bcopy (&s[O_LP1], &l, sizeof (l));
X  fprintf (f, "\tLocal1: 0x%x", l);
X  bcopy (&s[O_LP2], &l, sizeof (l));
X  fprintf (f, " Local2: 0x%x", l);
X  bcopy (&s[O_LP3], &l, sizeof (l));
X  fprintf (f, " Local3: 0x%x", l);
X  bcopy (&s[O_LP4], &l, sizeof (l));
X  fprintf (f, " Local4: 0x%x\n", l);
X  bcopy (&s[O_NDYNAMICS], &st, sizeof (st));
X  fprintf (f, "\tDynamics: %d", st);
X  bcopy (&s[O_NSTATICS], &st, sizeof (st));
X  fprintf (f, " Statics: %d", st);
X  bcopy (&s[O_ZONELIST], &l, sizeof (l));
X  fprintf (f, " Zonelist 0x%X\n", l);
X}
X
X/*
X * recv_gen - Generic receive routine.
X */
Xrecv_gen (p)
X
Xstruct fp_packet	*p;
X
X{
X  num_resp++;
X}
X
X/*
X * recv_pkt - Call appropriate function to deal with packet from fastpath.
X */
Xrecv_pkt (p)
X
Xstruct fp_packet	*p;
X
X{
X  char	buf[100];
X
X  if (p->fp_laptype != FP_TYPE) {
X      return;
X  }
X  if (destkbox != AT_Broadcast && p->fp_lapsrc != destkbox) {
X	/*
X	 * X_WHO responses from unconfigured fastpaths to broadcasts may come
X	 * in after we've already started talking to a real fastpath, so
X	 * just ignore all X_WHO responses with no error message.
X	 */
X      if (p->fp_scmd != X_WHO)
X	  logerr ("recv_pkt: Bogus response %d from %s\n",
X		  p->fp_scmd, ether_ntoa (&p->fp_shost));
X      return;
X  }
X  if (p->fp_cmd == FP_ACK) {
X      num_resp++;
X      return;
X  }
X  if (p->fp_cmd != FP_RESP)
X      return;
X  switch (p->fp_scmd) {
X    case X_COPYMEM :	if (recv_copyproc)
X			    (*recv_copyproc) (p);
X			break;
X
X    case X_HWSTATUS :	recv_hwstatus (p); break;
X
X    case X_PROMRAM :	recv_promram (p); break;
X
X    case X_RCSID :	recv_rcsid (p); break;
X
X    case X_STATE :	recv_state (p); break;
X
X    case X_VERSION :	recv_version (p); break;
X
X    case X_WHEREIS :	recv_whereis (p); break;
X
X    case X_WHERE2 :	recv_where2 (p); break;
X
X    case X_WHO :	recv_who (p); break;
X
X    case X_RESET :	recv_gen ();
X			break;
X
X    default :		ether_ntohost (buf, &p->fp_shost);
X			logerr ("recv_pkt: Received %d from %s\n",
X				p->fp_scmd, buf);
X			break;
X  }
X}
X
X/*
X * Send procedures.  Those that have no parameters are macros that invoke
X * send_gen(), defined in kbox.h.
X */
X
X/*
X * send_who - Broadcast who packet.
X */
Xint send_who ()
X
X{
X  int	i;
X
X  for (i = 0; i < MAXKBOX; i++)
X      if (kboxaddr[i])
X        kboxaddr[i]->aa = AT_Broadcast;
X  nit_timeout (60, &timeout);
X  return (send_pkt (NULL, (int) FP_CMD, X_WHO, 0, 5, &timeout));
X}
X
X/*
X * send_boot - Download file to kbox.
X */
Xint send_boot (kp, zonedata, zonelen)
X
Xstruct kbox	*kp;
Xunsigned char	*zonedata;
Xint		zonelen;
X
X{
X  FILE	*f;
X  int	l;
X  int	ch;
X  int	cmd;
X  int	scmd;
X  char	fn[MAXFN];
X  int	size = 0;
X  int	lastlen;
X  int	lastaddr;
X
X  sprintf (fn, "%s/%s", etcdir, kp->bootfile);
X  if ((f = fopen (fn, "r")) == NULL) {
X      logerr ("send_boot: %s to %s - %s\n", fn, kp->name, sys_errlist[errno]);
X      return (0);
X  }
X  nit_timeout (30, &timeout);
X  while ((cmd = fgetc (f)) > 0) {
X      if (cmd != 'S') {
X	  logerr ("send_boot: Bad S record %d\n", cmd);
X	  return (-1);
X      }
X      scmd = fgetc (f);
X      if (scmd == '8') {
X	/*
X	 * With KSTAR 8.0 it is necessary to download a zonelist with
X	 * KSTAR.  The following code converts the zonelist read from
X	 * the config file written by the Fastpath Manager into
X	 * s-records and sends them to the kbox.  The address where
X	 * the zonelist is downloaded is the first even address after
X	 * the last s-record, which is the address of the last s-record
X	 * plus its length, rounded up to the nearest even address.
X	 * The first s-record describes the following data, it looks like:
X	 *
X	 *	short num_elements;
X	 *	struct {
X	 *	  short	type;
X	 *	  short length;
X	 *	} element[NUMELEMENTS];
X	 *
X	 * For a zonelist the type is 1.  The second s-record contains
X	 * the zonelist itself.  These extra s-records get sent right
X	 * before the last s-record, which seems to have scmd == 8.
X	 * Much of the information on the format of the s-records and
X	 * their location was reverse engineered from packet traces,
X	 * I don't guarantee this code to be correct, although it
X	 * works for me.
X	 */
X	  lastlen = atox (fppkt.fastpath_data, 2);
X	  lastaddr = atox (&fppkt.fastpath_data[2], 6);
X	  zonelistp = (unsigned char *) (lastaddr + lastlen - 4);
X	  if ((int) zonelistp % 2 == 1)
X	      zonelistp++;
X	  makeelemlist (fppkt.fastpath_data, zonelen);
X	  if (debug)
X	      fprintf (stderr, "send_boot: %s\n", fppkt.fastpath_data);
X	  if (send_pkt (kp, 'S', '2', strlen(fppkt.fastpath_data),
X			5, &timeout) != 1)
X	      return (0);
X	  makezonedata (fppkt.fastpath_data, zonedata, zonelen);
X	  if (debug)
X	      fprintf (stderr, "send_boot: %s\n", fppkt.fastpath_data);
X	  if (send_pkt (kp, 'S', '2', strlen(fppkt.fastpath_data),
X			5, &timeout) != 1)
X	      return (0);
X      }
X      l = 0;
X      while ((ch = fgetc (f)) > 0 && ch != '\r' && ch != '\n')
X	  fppkt.fastpath_data[l++] = ch;
X      size += l;
X      if (send_pkt (kp, cmd, scmd, l, 5, &timeout) != 1)
X	  return (0);
X  }
X  if (debug)
X      fprintf (stderr, "send_boot: Downloaded %d bytes\n", size);
X  return (1);
X}
X
X/*
X * atox - Convert ascii string of length l to hex.
X */
Xint atox (s, l)
X
Xchar	*s;
Xint	l;
X
X{
X  int	r = 0;
X  int	i;
X
X  for (i = 0; i < l; i++)
X      r = (r << 4) | ((s[i] >= '0' && s[i] <= '9') ? s[i] - '0' :
X		      (s[i] >= 'a' && s[i] <= 'f') ? s[i] - 'a' + 10 :
X		      s[i] - 'A' + 10);
X  return (r);
X}
X
X/*
X * csumsrec - Compute checksum for s-record.
X */
Xunsigned char csumsrec (s, l)
X
Xunsigned char	*s;
Xint		l;
X
X{
X  unsigned char	sum;
X  static char	buf[3];
X
X  sum = 0;
X  while (l--)
X      sum += *s++;
X  sum ^= 0xFF;
X  return (sum);
X}
X
X/*
X * makeelemlist - Make element list srecord.
X */
Xmakeelemlist (p, l)
X
Xunsigned char	*p;
Xint		l;
X
X{
X  static elemlist	el = {1, 1, 0};
X  unsigned char		buf[BUFSIZ];
X
X  el.bytes = l + 2;				/* Don't know why +2	*/
X  bcopy (&zonelistp, buf, sizeof (zonelistp));	/* First byte is length */
X  *buf = 4 + sizeof (el);			/* so overwrite it	*/
X  bcopy (&el, &buf[4], sizeof (el));
X  buf[4 + sizeof (el)] = csumsrec (buf, 4 + sizeof (el));
X  hexify (buf, p, 4 + sizeof (el) + 1);
X}
X
X/*
X * makezonedata - Make zone data srecord.
X */
Xmakezonedata (p, data, l)
X
Xunsigned char	*p;
Xunsigned char	*data;
Xint		l;
X
X{
X  unsigned char	*addr;
X  unsigned char	buf[BUFSIZ];
X
X  addr = zonelistp + sizeof (elemlist);
X  bcopy (&addr, buf, sizeof (addr));
X  *buf = 4 + l;
X  bcopy (data, &buf[4], l);
X  buf[4 + l] = csumsrec (buf, 4 + l);
X  hexify (buf, p, 4 + l + 1);
X}
X
X/*
X * hexify - Convert buffer to hex ascii characters.
X */
Xhexify (buf, p, len)
X
Xunsigned char	*buf;
Xunsigned char	*p;
Xint		len;
X
X{
X  int	i;
X
X  for (i = 0; i < len; i++) {
X      p[2 * i] = HEX (buf[i] >> 4);
X      p[2 * i + 1] = HEX (buf[i] & 0xF);
X  }
X  p[2 * len] = '\0';
X}
X
X
X/*
X * send_getstate - Get state vector.
X */
Xint send_getstate (kp)
X
Xstruct kbox	*kp;
X
X{
X  struct fp_copy	copy;
X
X  copy.fpc_from = (char *) promram.fpr_state;
X  copy.fpc_to = (char *) -1;
X  copy.fpc_count = STATE_LENGTH;
X  bcopy (&copy, fppkt.fastpath_data, FPCOPYSIZE);
X  recv_copyproc = recv_getstate;
X  nit_timeout (30, &timeout);
X  return (send_pkt (kp, (int) FP_CMD, X_COPYMEM, FPCOPYSIZE, 5, &timeout));
X}
X
X/*
X * send_putstate - Put state vector.
X */
Xint send_putstate (kp)
X
Xstruct kbox	*kp;
X
X{
X  struct fp_copy	copy;
X
X  copy.fpc_from = (char *) -1;
X  copy.fpc_to = (char *) promram.fpr_state;
X  copy.fpc_count = htons (STATE_LENGTH);
X  bcopy (&copy, fppkt.fastpath_data, FPCOPYSIZE);
X  newstate[O_NODE] = kboxstate[O_NODE];
X  bcopy (newstate, &fppkt.fastpath_data[FPCOPYSIZE], STATE_LENGTH);
X  if (debug) {
X      fprintf (stderr, "send_putstate: Sending state...\n");
X      show_state (stderr, newstate);
X  }
X  recv_copyproc = recv_gen;
X  nit_timeout (30, &timeout);
X  return (send_pkt (kp, (int) FP_CMD, X_COPYMEM,
X		    FPCOPYSIZE + STATE_LENGTH, 5, &timeout));
X}
X
X/*
X * send_state - Send state request.
X */
Xint send_state (kp)
X
Xstruct kbox	*kp;
X
X{
X  static int	state = 0;
X
X  bcopy (&state, fppkt.fastpath_data, sizeof(state));
X  nit_timeout (30, &timeout);
X  return (send_pkt (kp, (int) FP_CMD, X_STATE, sizeof(state), 5, &timeout));
X}
X
X/*
X * send_hwstatus - Send hwstatus request.
X */
Xint send_hwstatus (kp)
X
Xstruct kbox	*kp;
X
X{
X  nit_timeout (30, &timeout);
X  return (send_pkt (kp, (int) FP_CMD, X_HWSTATUS, sizeof(long), 5, &timeout));
X}
X
X/*
X * send_gen - Generic send command.
X */
Xint send_gen (kp, scmd, retries)
X
Xstruct kbox	*kp;
Xint		scmd;
Xint		retries;
X
X{
X  nit_timeout (60, &timeout);
X  return (send_pkt (kp, (int) FP_CMD, scmd, 0, retries, &timeout));
X}
X
X/*
X * send_pkt - Send command packet (in fppkt).  Returns # of responders,
X *   or -1 on error.  If retries is 0, don't expect any response.
X */
Xint send_pkt (kp, cmd, scmd, len, retries, timeout)
X
Xstruct kbox	*kp;
Xint		cmd;
Xint		scmd;
Xint		len;
Xint		retries;
Xstruct timeval	*timeout;
X
X{
X  short			count;
X  int			h;
X  struct kbox		*p;
X  struct timeb		tstart;
X  struct timeb		tcur;
X  int			utotal;
X  int			ucur;
X
X  if (kp) {
X      if (kp->aa == AT_Broadcast) {
X	  logerr ("send_pkt: Invalid appletalk address for %s\n", kp->name);
X	  return (-1);
X      }
X      destkbox = kp->aa;
X      ether_copy (&kboxaddr[destkbox]->ea, &eh.ether_dhost);
X      if (retries == 0) {
X	  expected_resp = 0;
X	  retries = 1;
X      } else
X	  expected_resp = 1;
X  } else {
X      destkbox = AT_Broadcast;
X      ether_copy (&ebc, &eh.ether_dhost);
X      expected_resp = numkbox;
X  }
X  num_resp = 0;
X  fppkt.fastpath_lapdest = destkbox;
X  fppkt.fastpath_lapsrc = atalkaddr;
X  fppkt.fastpath_laptype = FP_TYPE;
X  fppkt.fastpath_cmd = cmd;
X  fppkt.fastpath_scmd = scmd;
X  count = htons (len + 4);
X  bcopy (&count, fppkt.fastpath_len, sizeof(count));
X  utotal = timeout->tv_sec * 1000000 + timeout->tv_usec;
X  for (count = 0; count < retries; count++) {
X      if (debug && count >= 1)
X	  fprintf (stderr, "send_pkt: Retransmitting cmd %d scmd %d\n",
X		   fppkt.fastpath_cmd, fppkt.fastpath_scmd);
X      nit_write (&eh, &fppkt, len + sizeof(struct ether_fastpath) - MAXFPPKT);
X      ftime (&tstart);
X      ucur = 0;
X      while (num_resp < expected_resp && utotal > ucur) {
X	  nit_dispatch (recv_pkt, 1, NULL, NULL, timeout);
X	  ftime (&tcur);
X	  ucur = (tcur.time - tstart.time) * 1000000 +
X		 (tcur.millitm - tstart.millitm) * 1000;
X      }
X      if (num_resp >= expected_resp)
X	  break;
X  }
X  if (num_resp != expected_resp)
X      if (destkbox == AT_Broadcast) {
X          for (h = 0; h < HTSIZE; h++)
X              for (p = kboxtab[h]; p; p = p->nxt)
X	          if (p->aa == AT_Broadcast)
X	              logerr ("send_pkt(%d,%d): No response from %s\n",
X			       cmd, scmd, p->name);
X      } else
X	  logerr ("send_pkt(%d,%d): No response from %s\n",
X		  cmd, scmd, kboxaddr[destkbox]->name);
X  return (num_resp);
X}
END_OF_FILE
if test 19050 -ne `wc -c <'cmd.c'`; then
    echo shar: \"'cmd.c'\" unpacked with wrong size!
fi
# end of 'cmd.c'
fi
if test -f 'getaa.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'getaa.c'\"
else
echo shar: Extracting \"'getaa.c'\" \(787 characters\)
sed "s/^X//" >'getaa.c' <<'END_OF_FILE'
X#ifndef lint
Xstatic char	*RCSid="$Header: /tmp_mnt/home/src/rand/etc/kboot/RCS/getaa.c,v 1.1 91/01/29 17:37:21 root Exp $";
X#endif lint
X
X/*
X * $Log:	getaa.c,v $
X * Revision 1.1  91/01/29  17:37:21  root
X * Initial revision
X * 
X */
X
X#include <stdio.h>
X#include <rpc/rpc.h>
X#include "aarpd.h"
X
X/*
X * getaa - Return appletalk address of "host".  If host is NULL get
X *   local host address.  Makes RPC call to aarpd on host.
X */
Xint getaa (host, aa)
X
Xchar	*host;
Xint	*aa;
X
X{
X  int	i;
X  int	s;
X
X  if (host == NULL)
X      host = "localhost";
X/*
X * Give aarpd a chance to register with portmap...
X */
X  for (i = 0; i < 3; i++)
X      if ((s = callrpc (host, GETAAPROG, GETAAVERS, GETAAPROC,
X	               xdr_void, 0, xdr_u_long, aa)) == 0)
X	  break;
X      else
X	  sleep (2);
X  return (s);
X}
END_OF_FILE
if test 787 -ne `wc -c <'getaa.c'`; then
    echo shar: \"'getaa.c'\" unpacked with wrong size!
fi
# end of 'getaa.c'
fi
if test -f 'icmp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'icmp.c'\"
else
echo shar: Extracting \"'icmp.c'\" \(3355 characters\)
sed "s/^X//" >'icmp.c' <<'END_OF_FILE'
X#ifndef lint
Xstatic char	*RCSid="$Header: /tmp_mnt/home/src/rand/etc/kboot/RCS/icmp.c,v 1.1 91/01/29 17:37:23 root Exp $";
X#endif lint
X
X/*
X * $Log:	icmp.c,v $
X * Revision 1.1  91/01/29  17:37:23  root
X * Initial revision
X * 
X */
X
X#include <stdio.h>
X#include <syslog.h>
X#include <sys/types.h>
X#include <sys/time.h>
X#include <sys/socket.h>
X#include <sys/errno.h>
X#include <netdb.h>
X#include <net/if.h>
X#include <netinet/in_systm.h>
X#include <netinet/in.h>
X#include <netinet/ip.h>
X#include <netinet/ip_icmp.h>
X#include <netinet/if_ether.h>
X#include "kbox.h"
X#include "config.h"
X
Xextern int	errno;
Xextern char	*sys_errlist[];
Xextern int	debug;
X
X/*
X * Globals.
X */
Xstruct icmp		icmp_pkt;
Xint			icmp_sock;
Xfd_set			icmp_fds;
X
X/*
X * icmp_init - Initialize icmp stuff.
X */
Xicmp_init ()
X
X{
X  if ((icmp_sock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
X      logerr ("icmp_init: %s\n", sys_errlist[errno]);
X      exit (1);
X  }
X  FD_SET (icmp_sock, &icmp_fds);
X  icmp_pkt.icmp_type = ICMP_ECHO;
X  icmp_pkt.icmp_code = 0;
X  icmp_pkt.icmp_seq = 0;
X  icmp_pkt.icmp_id = (getpid () & 0xFFFF);
X}
X
X
X/*
X * in_cksum - Compute IP checksum.
X */
Xu_short in_cksum (a, l)
X
Xu_short	*a;
Xint	l;
X
X{
X  int	sum = 0;
X
X  while (l > 1) {
X      sum += *a++;
X      l -= 2;
X  }
X  if (l == 1)
X      sum += *(u_char *) a;
X  sum += (sum >> 16);
X  return ((~sum) & 0xFFFF);
X}
X
X/*
X * icmp_ping - Ping kbox.
X */
Xint icmp_ping (kp)
X
Xstruct kbox	*kp;
X
X{
X  struct sockaddr_in	dest;
X  struct sockaddr_in	from;
X  static struct timeval	icmp_timeout = {PINGTIMEOUT, 0};
X  char			buf[BUFSIZ];
X  struct ip		*ip;
X  struct icmp		*p;
X  int			i;
X  int			r;
X  fd_set		readfds;
X
X  icmp_pkt.icmp_seq++;
X  icmp_pkt.icmp_cksum = 0;
X  icmp_pkt.icmp_cksum = in_cksum (&icmp_pkt, sizeof (icmp_pkt));
X  bzero (&dest, sizeof (dest));
X  dest.sin_family = AF_INET;
X  dest.sin_addr.s_addr = kp->ip.sin_addr.s_addr;
X  for (i = 0; i < PINGRETRIES; i++) {
X      p = NULL;
X	/*
X	 * Send ping
X	 */
X      if (debug)
X	  fprintf (stderr, "icmp_ping: Pinging %s id %d seq %d\n",
X		   inet_ntoa (dest.sin_addr), icmp_pkt.icmp_id,
X		   icmp_pkt.icmp_seq);
X      if (sendto (icmp_sock, &icmp_pkt, sizeof (icmp_pkt), 0,
X		  &dest, sizeof (dest)) != sizeof (icmp_pkt)) {
X	  logerr ("icmp_ping: sendto - %s\n", sys_errlist[errno]);
X	  exit (1);
X      }
X	/*
X	 * Wait for reply
X	 */
Xwaitreply:
X      readfds = icmp_fds;
X      r = select (icmp_sock+1, &readfds, NULL, NULL, &icmp_timeout);
X      if (r < 0) {
X	  logerr ("icmp_ping: select - %s\n", sys_errlist[errno]);
X	  exit (1);
X      } else if (r == 0)
X	  continue;
X	/*
X	 * Read reply
X	 */
X      r = sizeof (from);
X      if (recvfrom (icmp_sock, buf, BUFSIZ, 0, &from, &r) <= 0) {
X	  logerr ("icmp_ping: recvfrom - %s\n", sys_errlist[errno]);
X	  exit (1);
X      }
X	/*
X	 * Verify reply
X	 */
X      ip = (struct ip *) buf;
X      p = (struct icmp *) (buf + (ip->ip_hl << 2));
X      if (p->icmp_type == ICMP_ECHOREPLY &&
X	  p->icmp_id == icmp_pkt.icmp_id &&
X	  p->icmp_seq == icmp_pkt.icmp_seq &&
X	  from.sin_addr.s_addr == kp->ip.sin_addr.s_addr) {
X	  if (debug)
X	      fprintf (stderr, "icmp_ping: Reply from %s id %d seq %d\n",
X		       inet_ntoa (from.sin_addr), p->icmp_id, p->icmp_seq);
X	  return (1);
X      } else {
X	  if (debug)
X	      fprintf (stderr, "icmp_ping: Discarding packet from %s\n",
X		       inet_ntoa (from.sin_addr));
X	  goto waitreply;
X      }
X  }
X  return (0);
X}
END_OF_FILE
if test 3355 -ne `wc -c <'icmp.c'`; then
    echo shar: \"'icmp.c'\" unpacked with wrong size!
fi
# end of 'icmp.c'
fi
if test -f 'kboot.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'kboot.c'\"
else
echo shar: Extracting \"'kboot.c'\" \(12925 characters\)
sed "s/^X//" >'kboot.c' <<'END_OF_FILE'
X#ifndef lint
Xstatic char	*RCSid="$Header: /tmp_mnt/home/src/rand/etc/kboot/RCS/kboot.c,v 1.1 91/01/29 17:37:26 root Exp $";
X#endif lint
X
X/*
X * $Log:	kboot.c,v $
X * Revision 1.1  91/01/29  17:37:26  root
X * Initial revision
X * 
X */
X
X/*
X * kboot - Boot and configure fastpaths over an ethernet.  Depends on NIT
X *	   interface in SunOS 4.x.
X *
X * Author: Bob Schwartzkopf, The RAND Corporation.  Based on an earlier
X *	   version written by Dan Tappan at BBN that ran under SunOS 3.x.
X *
X * Comments, suggestions, patches, bug reports, etc. may be sent to
X * bobs@rand.org.
X */
X
X#include <stdio.h>
X#include <syslog.h>
X#include <fcntl.h>
X#include <signal.h>
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <sys/errno.h>
X#include <sys/termios.h>
X#include <netdb.h>
X#include <net/if.h>
X#include <netinet/in.h>
X#include <netinet/if_ether.h>
X#include "appletalk.h"
X#include "cmdidx.h"
X#include "cmdmacro.h"
X#include "kbox.h"
X#include "config.h"
X#include "patchlevel.h"
X
X#define VERSION		2
X
Xextern char			*rindex ();
Xstruct kbox			*get_kbox ();
Xextern int			errno;
Xextern char			*sys_errlist[];
Xextern struct ether_addr	myether;
X
X/*
X * Globals.
X */
Xchar			*progname;
Xstruct ether_addr	ebc = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
Xstruct ether_header	eh;
Xchar			*interface = NULL;
Xlong			atalkaddr;
Xchar			*etcdir = ETCDIR;
Xint			detached = 0;
Xint			debug = 0;
Xint			watchf = 0;
Xint			downloadf = 0;
Xint			configf = 0;
Xint			liststatef = 0;
Xint			savestatef = 0;
Xint			resetf = 0;
Xint			statusf = 0;
Xstruct kbox		*kboxtab[HTSIZE];
Xint			numkbox;
Xunsigned char		kboxstate[STATE_LENGTH];
Xunsigned char		newstate[STATE_LENGTH];
Xunsigned char		zonedata[MAXZONELIST];
Xint			zonelen;
Xint			pinginterval = PINGINTERVAL;
Xunsigned char		*zonelistp;
X
Xmain (argc, argv)
X
Xint	argc;
Xchar	**argv;
X
X{
X  int		i;
X  char		c;
X  char		*name = NULL;
X  struct kbox	*kp;
X
X  if ((progname = rindex (argv[0], '/')) == NULL)
X      progname = *argv;
X  else
X      progname++;
X  while (--argc > 0) {
X      argv++;
X      c = (*argv)[0] == '-' ? (*argv)[1] : (*argv)[0];
X      switch (c) {
X	case 'b' :
X	  if (argc <= 1)
X	      usage ();
X	  argv++; argc--;
X	  downloadf++;
X	  name = *argv;
X	  break;
X	case 'c' :
X	  if (argc <= 1)
X	      usage ();
X	  argv++; argc--;
X	  configf++;
X	  name = *argv;
X	  break;
X	case 'd' :
X	  debug++;
X	  break;
X	case 'i' :
X	  if (argc <= 1)
X	      usage ();
X	  argv++; argc--;
X	  interface = *argv;
X	  break;
X	case 'l' :
X	  if (argc <= 1)
X	      usage ();
X	  argv++; argc--;
X	  liststatef++;
X	  name = *argv;
X	  break;
X	case 'm' :
X	  if (argc <= 1)
X	      usage ();
X	  argv++; argc--;
X	  statusf++;
X	  name = *argv;
X	  break;
X	case 'p' :
X	  if (argc <= 1)
X	      usage ();
X	  argv++; argc--;
X	  pinginterval = atoi (*argv);
X	  break;
X	case 'r' :
X	  if (argc <= 1)
X	      usage ();
X	  argv++; argc--;
X	  resetf++;
X	  name = *argv;
X	  break;
X	case 's' :
X	  if (argc <= 1)
X	      usage ();
X	  argv++; argc--;
X	  savestatef++;
X	  name = *argv;
X	  break;
X	case 'v' :
X	  printf ("%s version %d.%d\n", progname, VERSION, PATCHLEVEL);
X	  exit (0);
X	case 'w' :
X	  watchf++;
X	  break;
X	default :
X	  usage ();
X	  break;
X      }
X  }
X  if (watchf && !debug) {		/* Detach ourselves		*/
X      detached++;
X      if (fork ())
X	  exit (0);
X      if ((i = open ("/dev/tty", O_RDONLY)) >= 0)
X	  ioctl (i, TIOCNOTTY, 0);
X      i = getdtablesize ();
X      while (--i >= 0)
X	  close (i);
X      open ("/dev/null", 0);
X      dup2 (0, 1);
X      dup2 (0, 2);
X      openlog (progname, 0, LOG_DAEMON);
X      writepid ();
X  }
X  if ((i = getaa (NULL, &atalkaddr)) != 0) {
X      logerr (clnt_sperrno (i));
X      exit (1);
X  }
X  if (debug)
X      fprintf (stderr, "main: Using appletalk address %d\n", atalkaddr);
X  nit_init (interface);
X  ht_init ();
X  readcf ();
X  ether_copy (&myether, &eh.ether_shost);
X  eh.ether_type = htons (P_AppleTalk);
X  if (watchf)
X      watch ();
X  else  {
X      if (!name)
X	  usage ();
X      if (!(kp = get_kbox (name))) {
X	  logerr ("main: No such kbox %s\n", name);
X	  exit (1);
X      }
X      nit_open (0, P_AppleTalk);
X      if (downloadf)
X	  download (kp);
X      else if (configf)
X	  config (kp);
X      else if (savestatef)
X	  savestate (kp);
X      else if (resetf)
X	  reset (kp);
X      else if (liststatef)
X	  liststate (kp);
X      else if (statusf)
X	  status (kp);
X  }
X  exit (0);
X}
X
X/*
X * reconfig - Free old kbox hash table and reread config file.
X */
Xreconfig ()
X
X{
X  if (debug)
X      fprintf (stderr, "reconfig: Reading config file\n");
X  ht_free ();
X  readcf ();
X}
X
X/*
X * watch - Watch kboxes, rebooting if not answering pings.
X */
Xwatch ()
X
X{
X  int		i;
X  struct kbox	*kp;
X
X  signal (SIGHUP, reconfig);
X  icmp_init ();
X  while (1) {
X      for (i = 0; i < HTSIZE; i++)
X	  for (kp = kboxtab[i]; kp; kp = kp->nxt)
X	      if (!icmp_ping (kp)) {
X		  logerr ("watch: %s not responding, loading...\n", kp->name);
X		  nit_open (0, P_AppleTalk);
X		  download (kp);
X		  nit_close ();
X	      }
X      sleep (pinginterval);
X  }
X}
X
X/*
X * download - Download kstar to kbox.
X */
Xdownload (kp)
X
Xstruct kbox	*kp;
X
X{
X  int	i;
X
X  if ((i = readstate (kp, newstate, zonedata)) != STATE_LENGTH) {
X      logerr ("config: State file wrong length %d for %s\n", i, kp->name);
X      return;
X  }
X  if (send_who () < 0)
X      return;
X  if (send_reset (kp) < 0)
X      return;
X  sleep (3);			/* Give kbox time to reset	*/
X  if (send_who () < 0)
X      return;
X  if (send_promram (kp) <= 0)
X      return;
X  if (send_boot (kp, zonedata, zonelen) <= 0)
X      return;
X  if (send_getstate (kp) <= 0)
X      return;
X  newstate[O_NODE] = kboxstate[O_NODE];
X  newstate[O_ETNODE] = kboxstate[O_ETNODE];
X  strcpy (&newstate[O_NAME], kp->name);
X  strcpy (&newstate[O_FILE], kp->bootfile);
X  strcpy (&newstate[O_CONFIG], kp->conffile);
X  if (debug)
X      fprintf (stderr, "download: Setting zonelist to %X\n", zonelistp);
X  bcopy (&zonelistp, &newstate[O_ZONELIST], sizeof (zonelistp));
X  if (send_putstate (kp) <= 0)
X      return;
X  send_execute (kp);
X}
X
X/*
X * config - Config kbox.
X */
Xconfig (kp)
X
Xstruct kbox	*kp;
X
X{
X  int	i;
X
X  if ((i = readstate (kp, newstate, zonedata)) != STATE_LENGTH) {
X      logerr ("config: State file wrong length %d for %s\n", i, kp->name);
X      return;
X  }
X  if (send_who () < 0)
X      return;
X  if (send_exprom (kp) < 0)
X      return;
X  if (send_who () < 0)
X      return;
X  if (send_promram (kp) <= 0)
X      return;
X  if (send_getstate (kp) <= 0)
X      return;
X  newstate[O_NODE] = kboxstate[O_NODE];
X  newstate[O_ETNODE] = kboxstate[O_ETNODE];
X  strncpy (&newstate[O_NAME], &kboxstate[O_NAME], SNAMESIZE);
X  strncpy (&newstate[O_FILE], &kboxstate[O_FILE], SNAMESIZE);
X  strncpy (&newstate[O_CONFIG], kp->conffile, FNAMESIZE);
X  if (send_putstate (kp) <= 0)
X      return;
X  if (send_who () < 0)
X      return;
X  send_execute (kp);
X}
X
X/*
X * savestate - Read state for kbox and save to file.
X */
Xsavestate (kp)
X
Xstruct kbox	*kp;
X
X{
X  char	fn[MAXFN];
X  int	fd;
X
X  if (send_who () < 0)
X      return;
X  if (send_promram (kp) <= 0)
X      return;
X  if (send_getstate (kp) <= 0)
X      return;
X  sprintf (fn, "%s/%s.state", etcdir, kp->name);
X  if ((fd = open (fn, O_WRONLY|O_CREAT, 0644)) == -1) {
X      logerr ("savestate: %s - %s\n", fn, sys_errlist[errno]);
X      return;
X  }
X  write (fd, kboxstate, STATE_LENGTH);
X  close (fd);
X}
X
X/*
X * liststate - Read state for kbox and print.
X */
Xliststate (kp)
X
Xstruct kbox	*kp;
X
X{
X  char	fn[MAXFN];
X  int	fd;
X
X  if (send_who () < 0)
X      return;
X  if (send_promram (kp) <= 0)
X      return;
X  if (send_getstate (kp) <= 0)
X      return;
X  show_state (stdout, kboxstate);
X}
X
X/*
X * reset - Reset kbox.
X */
Xreset (kp)
X
Xstruct kbox	*kp;
X
X{
X  if (send_who () < 0)
X      return;
X  send_reset (kp);
X}
X
X/*
X * status - Read and print miscellaneous things from kbox.
X */
Xstatus (kp)
X
Xstruct kbox	*kp;
X
X{
X  if (send_who () < 0)
X      return;
X  if (send_version (kp) <= 0)
X      return;
X  if (send_whereis (kp) <= 0)
X      return;
X  if (send_where2 (kp) <= 0)
X      return;
X  if (send_rcsid (kp) <= 0)
X      return;
X  if (send_state (kp) <= 0)
X      return;
X  if (send_hwstatus (kp) <= 0)
X      return;
X}
X
X/*
X * writepid - Save pid to file.
X */
Xwritepid ()
X
X{
X  char	fn[MAXFN];
X  FILE	*f;
X
X  sprintf (fn, "%s/kboot.pid", etcdir);
X  if ((f = fopen (fn, "w")) == NULL)
X      logerr ("main: Can't write pid file %s\n", fn);
X  else {
X      fprintf (f, "%d\n", getpid ());
X      fclose (f);
X  }
X}
X
X/*
X * usage - Print usage and die.
X */
Xusage ()
X
X{
X  fprintf (stderr, "Usage: %s options\n", progname);
X  exit (1);
X}
X
X/*
X * hash - Return hash index into kbox hash table.
X */
Xint hash (s)
X
Xregister char	*s;
X
X{
X  register int	h;
X
X  while (*s)
X      h = (h << 1) ^ *s++;
X  return (h % HTSIZE);
X}
X
X/*
X * ht_init - Init kbox hash table.
X */
Xht_init ()
X
X{
X  int	i;
X
X  for (i = 0; i < HTSIZE; i++)
X      kboxtab[i] = NULL;
X}
X
X/*
X * ht_free - Clear out hash table.
X */
Xht_free ()
X
X{
X  int		i;
X  struct kbox	*kp;
X  struct kbox	*nkp;
X
X  for (i = 0; i < HTSIZE; i++) {
X      for (kp = kboxtab[i]; kp; kp = nkp) {
X	  nkp = kp->nxt;
X	  free (kp);
X      }
X      kboxtab[i] = NULL;
X  }
X}
X	  
X/*
X * add_kbox - Add kbox to hash table.
X */
Xadd_kbox (name, bootfile)
X
Xchar	*name;
Xchar	*bootfile;
X
X{
X  int			h;
X  struct kbox		*kp;
X  char			*p;
X  struct hostent	*he;
X  struct ether_addr	e;
X
X  if (ether_hostton (name, &e) != 0) {
X      logerr ("add_kbox: Can't find %s in ethers file\n", name);
X      return;
X  }
X  if ((he = gethostbyname (name)) == NULL) {
X      logerr ("add_kbox: Can't find %s in host table\n", name);
X      return;
X  }
X  if ((kp = (struct kbox *) malloc (sizeof (struct kbox))) == NULL) {
X      logerr ("add_kbox: No memory for %s in table\n", name);
X      exit (1);
X  }
X  h = hash (name);
X  kp->nxt = kboxtab[h];
X  kboxtab[h] = kp;
X  strcpy (kp->name, name);
X  strcpy (kp->bootfile, bootfile);
X  kp->aa = AT_Broadcast;	/* Set to bogus value until get from kb	*/
X  ether_copy (&e, &kp->ea);
X  bcopy (he->h_addr, &kp->ip.sin_addr.s_addr, he->h_length);
X}
X
X/*
X * get_kbox - Find kbox in hash table.
X */
Xstruct kbox *get_kbox (name)
X
Xchar	*name;
X
X{
X  register struct kbox	*kp;
X
X  kp = kboxtab[hash (name)];
X  while (kp)
X      if (!strcmp (name, kp->name))
X	  return (kp);
X      else
X	  kp = kp->nxt;
X  return (NULL);
X}
X
X/*
X * readcf - Read config file.
X */
Xreadcf ()
X
X
X{
X  FILE	*f;
X  char	fn[MAXFN];
X  char	name[MAXHOSTNAME];
X  char	bootfile[MAXFN];
X
X  sprintf (fn, "%s/kbootcf", etcdir);
X  if ((f = fopen (fn, "r")) == NULL) {
X      logerr ("readcf: %s - %s\n", fn, sys_errlist[errno]);
X      exit (1);
X  }
X  while (!feof (f)) {
X      numkbox++;
X      if (fscanf (f, "%s %s\n", name, bootfile) != 2) {
X	  logerr ("readcf: Badly formatted config file '%s' line %d\n",
X		  fn, numkbox);
X	  exit (1);
X      }
X      add_kbox (name, bootfile);
X  }
X  fclose (f);
X}
X
X/*
X * readstate - Read kbox config.  Returns length.  Looks for ascii file
X *   generated by fastpath manager first, then looks for binary file
X *   made by savestate().
X */
Xint readstate (kp, state, zonedata)
X
Xstruct kbox	*kp;
Xunsigned char	*state;
Xunsigned char	*zonedata;
X
X{
X  FILE		*f;
X  int		c;
X  unsigned char	*cp;
X  int		fd;
X  int		len;
X
X  zonelen = 0;
X  sprintf (kp->conffile, "%s/%s.config", etcdir, kp->name);
X  if ((f = fopen (kp->conffile, "r")) == NULL) {
X      sprintf (kp->conffile, "%s/%s.state", etcdir, kp->name);
X      if ((fd = open (kp->conffile, O_RDONLY)) == -1) {
X	  logerr ("readstate: Can't read state file for %s\n", kp->name);
X	  return;
X      }
X      len = read (fd, state, STATE_LENGTH);
X      close (fd);
X  } else {
X      cp = state;
X      len = 0;
X      while ((c = fgetc (f)) >= 0) {
X	  if (c == '*') {
X	      while ((c = fgetc (f)) >= 0 && c != '\n' && c != '\r');
X	      continue;
X	  }
X	  while (c >= 0 && c != '\n' && c != '\r') {
X	      *cp = fromhex (c) << 4;
X	      *cp++ |= fromhex (c = fgetc (f));
X	      c = fgetc (f);
X	  }
X	  if (cp - state == STATE_LENGTH) {
X	      zonelen = readzones (f, c, zonedata);
X	      break;
X	  }
X      }
X      fclose (f);
X      len = cp - state;
X  }
X  if (zonelen == 0) {
X      zonedata[0] = 0;
X      zonedata[1] = 0;
X      zonelen = 2;
X  }
X  return (len);
X}
X
X/*
X * readzones - Read zone list from config file, c is first char.
X */
Xint readzones (f, c, zonedata)
X
XFILE		*f;
Xint		c;
Xunsigned char	*zonedata;
X
X{
X  unsigned char	*cp;
X  unsigned char	*lenp;
X  short		numzones;
X
X  cp = zonedata + 2;			/* Skip # zones		*/
X  numzones = 0;
X  while ((c = fgetc (f)) >= 0) {
X      if (c == '\r' || c == '\n')
X	  continue;
X      if (c == '*') {
X	  while ((c = fgetc (f)) >= 0 && c != '\n' && c != '\r');
X	  continue;
X      }
X      lenp = cp++;
X      while (c >= 0 && c != '\n' && c != '\r') {
X	  *cp++ = c;
X	  c = fgetc (f);
X      }
X      *lenp = (cp - lenp) - 1;
X      numzones++;
X  }
X  bcopy (&numzones, zonedata, sizeof (numzones));
X  return (cp - zonedata);
X}
X
X/*
X * fromhex - Convert char from hex to decimal.
X */
Xint fromhex (c)
X
Xchar	c;
X
X{
X  if (c >= '0' && c <= '9')
X      return (c - '0');
X  else if (c >= 'a' && c <= 'f')
X      return (c - 'a' + 10);
X  else
X      return (c - 'A' + 10);
X}
END_OF_FILE
if test 12925 -ne `wc -c <'kboot.c'`; then
    echo shar: \"'kboot.c'\" unpacked with wrong size!
fi
# end of 'kboot.c'
fi
if test -f 'nitlib.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'nitlib.c'\"
else
echo shar: Extracting \"'nitlib.c'\" \(6224 characters\)
sed "s/^X//" >'nitlib.c' <<'END_OF_FILE'
X#ifndef lint
Xstatic char	*RCSid="$Header: /tmp_mnt/home/src/rand/etc/kboot/RCS/nitlib.c,v 1.1 91/01/29 17:37:28 root Exp $";
X#endif lint
X
X/*
X * $Log:	nitlib.c,v $
X * Revision 1.1  91/01/29  17:37:28  root
X * Initial revision
X * 
X */
X
X#include <stdio.h>
X#include <syslog.h>
X#include <sys/types.h>
X#include <sys/time.h>
X#include <sys/socket.h>
X#include <sys/file.h>
X#include <sys/signal.h>
X#include <netdb.h>
X#include <net/if.h>
X#include <sys/ioctl.h>
X#include <sys/stropts.h>
X#include <net/nit_if.h>
X#include <net/nit_pf.h>
X#include <netinet/in.h>
X#include <netinet/in_systm.h>
X#include <netinet/if_ether.h>
X#include <net/packetfilt.h>
X#include "config.h"
X
X#define MAXPKT		1500
X#define OFFSET(t,f)	((int) &(((t *) NULL)->f))
X
Xextern char	*malloc ();
Xextern char	*strdup ();
X
Xextern int	debug;
Xextern int	detached;
Xextern int	errno;
Xextern char	*sys_errlist[];
X
X/*
X * Globals.
X */
Xstruct ether_addr	myether;
Xint			nit_fd;
Xchar			nitbuf[MAXPKT];
Xint			numfds;
Xchar			*interface;
X
X/*
X * nit_init - Initialize nit library.
X */
Xnit_init (dev)
X
Xchar	*dev;
X
X{
X  struct ifconf		ifc;
X  int			s;
X  char			buf[BUFSIZ];
X
X  numfds = getdtablesize ();
X  if (dev == NULL) {
X      if ((s = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
X            logerr ("nit_init: socket - %s\n", sys_errlist[errno]);
X            exit(1);
X      }
X      ifc.ifc_len = BUFSIZ;
X      ifc.ifc_buf = buf;
X      if (ioctl (s, SIOCGIFCONF, (caddr_t) &ifc) < 0) {
X          logerr ("nit_init: ioctl(SIOCGIFCONF) - %s\n", sys_errlist[errno]);
X          exit(1);
X      }
X      close(s);
X      interface = strdup (ifc.ifc_req->ifr_name);
X  } else
X      interface = dev;
X  if (debug)
X      fprintf (stderr, "nit_init: Using interface %s\n", interface);
X}
X
X/*
X * nit_open - Open NIT device and bind to an interface.
X *   Prints an error and exits if there's a problem.
X */
Xnit_open (snaplen, pkttype)
X
Xint	snaplen;
Xint	pkttype;
X
X{
X  int			s;
X  struct packetfilt	pf;
X  u_short		*pfp;
X  struct ifreq		ifr;
X
X  if ((nit_fd = open ("/dev/nit", O_RDWR)) < 0) {	/* Open NIT dev	*/
X      logerr ("nit_open: open(/dev/nit) - %s\n", sys_errlist[errno]);
X      exit (1);
X  }
X  if (ioctl (nit_fd, I_SRDOPT, (caddr_t) RMSGD) < 0) {	/* Discrete msgs*/
X      logerr ("nit_open: ioctl(I_SRDOPT) - %s\n", sys_errlist[errno]);
X      exit (1);
X  }
X  if (pkttype) {
X      if (ioctl (nit_fd, I_PUSH, "pf") < 0) {	/* Setup packet filter	*/
X          logerr ("nit_open: ioctl(I_PUSH: pf) - %s\n", sys_errlist[errno]);
X          exit (1);
X      }
X      pfp = pf.Pf_Filter;
X      *pfp++ = ENF_PUSHWORD +
X	OFFSET (struct ether_header, ether_type) / sizeof (u_short);
X      *pfp++ = ENF_PUSHLIT | ENF_EQ;
X      *pfp++ = htons (pkttype);
X      pf.Pf_FilterLen = ((int) pfp - (int) pf.Pf_Filter) / sizeof (u_short);
X      if (ioctl (nit_fd, NIOCSETF, (caddr_t) &pf) < 0) {
X          logerr ("nit_open: ioctl(NIOCSETF) - %s\n", sys_errlist[errno]);
X          exit (1);
X      }
X  }
X  strncpy (ifr.ifr_name, interface, sizeof ifr.ifr_name);
X  ifr.ifr_name[sizeof ifr.ifr_name - 1] = '\0';
X  if (ioctl (nit_fd, NIOCBIND, &ifr) < 0) {
X      logerr ("nit_open: ioctl(NIOCBIND) - %s\n", sys_errlist[errno]);
X      exit(1);
X  }
X						/* Get ethernet address	*/
X  if (ioctl (nit_fd, SIOCGIFADDR, (caddr_t) &ifr) < 0) {
X      logerr ("nit_open: ioctl(SIOCGIFADDR) - %s\n", sys_errlist[errno]);
X      exit (1);
X  }
X  ether_copy (ifr.ifr_addr.sa_data, &myether);
X  if (snaplen > 0) {                            /* Set snapshot length  */
X      if (ioctl (nit_fd, NIOCSSNAP, (caddr_t) &snaplen) < 0) {
X          logerr ("nit_open: ioctl (NIOCSSNAP) - %s\n", sys_errlist[errno]);
X          exit (1);
X      }
X  }
X}
X
X/*
X * nit_close - Close nit file descriptor.
X */
Xnit_close ()
X
X{
X  close (nit_fd);
X}
X
X/*
X * timeout - Return timeval structure for timeout specified in ticks for
X *   reads from nit device.  Ticks are 1/60 of a second.  A timeout of 0
X *   means reads will not timeout.
X */
Xnit_timeout (ticks, tv)
X
Xint		ticks;
Xstruct timeval	*tv;
X
X{
X  tv->tv_sec = ticks / 60;			/* Set timeout		*/
X  tv->tv_usec = ((ticks * 1000000) / 60) % 1000000;
X}
X
X/*
X * nit_write - Send a raw ethernet packet.
X */
Xnit_write (eh, pkt, len)
X
Xstruct ether_header	*eh;
Xcaddr_t			pkt;
Xint			len;
X
X{
X  struct sockaddr	sa;
X  struct strbuf		cbuf;
X  struct strbuf		dbuf;
X
X  sa.sa_family = AF_UNSPEC;
X  bcopy (eh, sa.sa_data, sizeof (*eh));
X  cbuf.len = sizeof sa;
X  cbuf.buf = (caddr_t) &sa;
X  dbuf.len = len;
X  dbuf.buf = pkt;
X  if (putmsg (nit_fd, &cbuf, &dbuf, 0) < 0) {
X      logerr ("nit_write: putmsg - %s\n", sys_errlist[errno]);
X      exit (1);
X  }
X}
X
X/*
X * nit_dispatch - Read and process n packets.  If n is 0 read forever.
X *   Calls "func" for each packet with the arguments:
X *
X *	(*func) (pp, pl)
X *	  pp = packet pointer
X *	  pl = length of packet
X *
X *   If an application is waiting for input on other file descriptors
X *   they can be specified in "fds".  "fdfunc" will be called with
X *   the set of descriptors that have input waiting before the current
X *   packet is processed.  Returns 0 on timeout, 1 otherwise.
X */
Xint nit_dispatch (func, n, fds, fdfunc, timeout)
X
Xint		(*func)();
Xint		n;
Xfd_set		*fds;
Xint		(*fdfunc)();
Xstruct timeval	*timeout;
X
X{
X  int		i;
X  int		numpkts;
X  fd_set	fdset;
X  fd_set	rfds;
X
X  numpkts = 0;
X  if (fds)
X      fdset = *fds;
X  else
X      FD_ZERO (&fdset);
X  FD_SET (nit_fd, &fdset);
X  while (n == 0 || numpkts < n) {
X      rfds = fdset;
X      i = select (numfds, &rfds, NULL, NULL, timeout);
X      if (i == 0)
X	  return (0);			/* Timeout			*/
X      if (FD_ISSET (nit_fd, &rfds)) {
X	  numpkts++;
X	  FD_CLR (nit_fd, &rfds);
X	  if (i > 1)
X	      (*fdfunc) (&rfds);
X      } else {
X	  (*fdfunc) (&rfds);
X	  continue;
X      }
X      if ((i = read (nit_fd, nitbuf, MAXPKT)) < 0) {
X          lseek (nit_fd, 0, 0);		/* File pointer may've wrapped	*/
X          if ((i = read (nit_fd, nitbuf, MAXPKT)) < 0) {
X	      logerr ("nit_dispatch: read - %s\n", sys_errlist[errno]);
X	      exit (1);
X          }
X      }
X      (*func) (nitbuf, i);
X  }
X  return (1);
X}
X
X/*
X * logerr - Log error to stderr (if debug) or syslog.
X */
Xlogerr (fmt, a1, a2, a3)
X
Xchar	*fmt;
Xchar	*a1;
Xchar	*a2;
Xchar	*a3;
X
X{
X  if (detached)
X      syslog (LOG_ERR, fmt, a1, a2, a3);
X  else
X      fprintf (stderr, fmt, a1, a2, a3);
X}
END_OF_FILE
if test 6224 -ne `wc -c <'nitlib.c'`; then
    echo shar: \"'nitlib.c'\" unpacked with wrong size!
fi
# end of 'nitlib.c'
fi
echo shar: End of archive 1 \(of 3\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 3 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 3 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0