Imported Upstream version 5.1 upstream upstream/5.1
authorBdale Garbee <bdale@gag.com>
Tue, 20 May 2008 05:11:09 +0000 (23:11 -0600)
committerBdale Garbee <bdale@gag.com>
Tue, 20 May 2008 05:11:09 +0000 (23:11 -0600)
41 files changed:
.eggrc [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
basket.c [new file with mode: 0644]
basketrc.sample [new file with mode: 0644]
byteorder.h [new file with mode: 0644]
crc16.c [new file with mode: 0644]
egg.c [new file with mode: 0644]
eggrc.sample [new file with mode: 0644]
eggui.c [new file with mode: 0644]
eggui.h [new file with mode: 0644]
errnos.h [new file with mode: 0644]
examine.c [new file with mode: 0644]
genlib.c [new file with mode: 0644]
genlib.h [new file with mode: 0644]
global.h [new file with mode: 0644]
hw_pear.c [new file with mode: 0644]
hwapi.h [new file with mode: 0644]
ipproto.c [new file with mode: 0644]
ipproto.h [new file with mode: 0644]
lecuyer.c [new file with mode: 0644]
lecuyer.h [new file with mode: 0644]
network.c [new file with mode: 0644]
network.h [new file with mode: 0644]
reg.h [new file with mode: 0644]
reg_orion.c [new file with mode: 0644]
reg_pear.c [new file with mode: 0644]
reg_pseudo.c [new file with mode: 0644]
regs.h [new file with mode: 0644]
regtable.h [new file with mode: 0644]
regtest.c [new file with mode: 0644]
sample.basketrc [new file with mode: 0644]
sample.eggrc [new file with mode: 0644]
sample.pppscript [new file with mode: 0755]
storage.c [new file with mode: 0644]
storage.h [new file with mode: 0644]
testmain.c [new file with mode: 0644]
testudp.c [new file with mode: 0644]
usleep.c [new file with mode: 0644]
version.h [new file with mode: 0644]
xdsub.c [new file with mode: 0644]

diff --git a/.eggrc b/.eggrc
new file mode 100644 (file)
index 0000000..b8c44bc
--- /dev/null
+++ b/.eggrc
@@ -0,0 +1,86 @@
+# Configuration file for egg collection software
+# This specifies the egg configuration information, the contact
+# information for its basket(s), and initial data acquisition parameters.
+
+# Each line consists of a case-sensitive keyword and a series of
+# options.  Defined keywords are: 
+#
+#   EGG <name> <id> <ip addr> <primbasket> <conntype> <connival> <url>
+#
+# The primary basket <primbasket> is the name of the basket (defined
+# below) that should be contacted retrieve data from this Egg.
+# The connection type <conntype> is "PERM" (permanent) or "DND"
+# (dial-and-drop).  So far the code does not support the DND option.
+# The connection interval <connival> determines the time (in minutes)
+# between transmission attempts.  If specified, and not equal to
+# ".", <url> gives a URL (for example http://www.somesite.net/page.html)
+# which will be linked to the egg <name> in status reports.  Note that
+# this specification has meaning only in the .basketrc file.  For
+# compatibility, it is permitted in an .eggrc file, but is ignored.
+#
+#   BASKET <name> <ip addr>
+#
+# This entry tells us about the existence of a set of baskets.  The
+# basket named as primary is the one which will be contacted
+# initially. 
+#
+#   PROTOCOL <samprec> <secrec> <recpkt> <trialsz>
+#
+# Specify the default data collection protocol.  The arguments are 
+# <samprec> samples per record (1-10, though this should always be 5-10),
+# <secrec> seconds per records (1-3000),
+# <recpkt> records per packet (1-60), and 
+# <trialsz> bits per sample (32-255).
+#
+#   REG <type> <port> <baud>
+#
+# Specify hardware device.  Supported types include "PEAR" only at
+# this time.  Port is serial port number (e.g. 1 for /dev/ttyS1);
+# <baud> is baud rate.
+#
+#   NETUP <script> <args> ...
+#   NETDOWN <script> <args> ...
+#
+# Provide script files to be run to bring up and tear down the network
+# connection on demand.  These will only be used if connection type is
+# DND.
+
+#EGG halley 1 10.0.0.111 halley PERM 1
+#BASKET halley 10.0.0.111
+#EGG halley 1 10.0.0.111 mercury DND 3
+#BASKET mercury 10.0.0.125
+
+#EGG noosphere 28 128.112.35.133 noosphere PERM 1
+#BASKET noosphere 128.112.35.133
+
+#   diesse -> diesse
+#EGG diesse 37 193.8.230.134 diesse PERM 1
+#BASKET diesse 193.8.230.134
+
+#   noosphere -> diesse
+EGG noosphere 28 128.112.35.133 diesse PERM 1
+BASKET diesse 193.8.230.134
+
+#   throop -> diesse
+#EGG throop 1003 193.8.230.132 diesse PERM 1 http://www.fourmilab.ch/
+#BASKET diesse 193.8.230.134
+
+#   jura -> jura
+#EGG jura 1004 193.8.230.130 jura PERM 1 http://www.fourmilab.ch/
+#BASKET jura 193.8.230.130
+
+#   diesse -> noosphere
+#EGG diesse.fourmilab.ch 37 193.8.230.134 noosphere PERM 1
+#BASKET noosphere 128.112.35.133
+
+#EGG diesse 37 193.8.230.134 jura PERM 1
+#BASKET jura 193.8.230.130
+
+#BASKET tonga 209.157.90.137
+#BASKET tonga1 209.157.90.138
+PROTOCOL 10 10 6 200
+#REG PEAR 2 9600
+REG PSEUDO 1 9600
+#REG ORION 1 9600
+NETUP pppscript up
+NETDOWN pppscript down
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..e894eb5
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,142 @@
+
+#   Debug options you can define on the following line:
+
+#      -DALT_UI    --  Select an alternative user interface
+#                      that doesn't require curses.  This allows
+#                      running an egg even if there's a problem
+#                      with terminal configuration.
+#
+#      -DDEBUG     --  General debug output
+#
+#      -DHEXDUMP   --  Dump network packets sent and received
+#                      in hexadecimal, identifying the recipient
+#                      or sender by IP address.  Handy when
+#                      tracking down byte alignment problems.
+#
+#      -DNICE=x    --  Priority increment (positive value) which
+#                      eggsh raises to while collecting data when
+#                      run by super-user.  Default is 10.  Setting
+#                      -DNICE=0 disables the priority adjustment
+#                      mechanism.
+#
+#      -DNO_UI     --  Disable user interface in the egg.  This
+#                      is handy when you want to watch other
+#                      debug output.
+#
+#      -DPACKETDUMP -- Interpreted dump of packets.
+#
+#      -DSTORAGE_DEBUG  -- Debug output from storage.c database
+#                      functions.
+#
+#      -DTESTPORT  --  Use EGGPORT = 2074, BASKETPORT = 2075.
+#                      This permits testing an experimental
+#                      version of eggsh and basket on a machine
+#                      which is running production version(s)
+#                      on the standard ports.
+#
+#      -DUSLEEP    --  Use a built-in emulation of usleep()
+#                      based on setitimer() instead of the
+#                      system library function (which may not
+#                      exist on all flavours of Unix).
+
+#DEBUGOPTIONS = -DDEBUG -DPACKETDUMP -DTESTPORT -DHEXDUMP -DNO_UI -DSTORAGE_DEBUG
+
+#DEBUGOPTIONS = -DTESTPORT -DDEBUG -DPACKETDUMP -DNO_UI -DALT_UI
+
+#DEBUGOPTIONS = -DTESTPORT
+
+#DEBUGOPTIONS =
+
+#DEBUGOPTIONS = -DTESTPORT -DALT_UI -DNO_UI
+
+#DEBUGOPTIONS = -DDEBUG -DALT_UI -DNO_UI
+
+#DEBUGOPTIONS = -DDEBUG -DNO_UI -DHEXDUMP
+
+# Build for throop/Orion egg
+#DEBUGOPTIONS = -DNO_UI -DALT_UI -DDEBUG -DUSLEEP -DFLUSH
+
+# Current debug options
+#DEBUGOPTIONS = -DNO_UI -DALT_UI -DDEBUG -DTESTPORT
+
+DEBUGOPTIONS = -DREPORT=1 -DEGG_DYNAMIC
+
+#   For Linux with GCC
+CC = gcc
+PFLAGS = -g -ansi -Wall -DLinux -DUSLEEP -D_GNU_SOURCE
+LIBS = -lncurses
+
+#   For Solaris 2.6 with Sun compiler
+#CC = cc
+#PFLAGS = -g -DSolaris
+#LIBS = -lcurses -lsocket -lnsl
+
+#   Silicon Graphics Irix 5.3 with SGI compiler.  Yes
+#   we do define "Solaris" along with "Irix" for such a build.
+#CC = cc
+#PFLAGS = -g -DSolaris -DIrix -DUSLEEP
+#LIBS = -lcurses
+
+CFLAGS = $(PFLAGS) $(DEBUGOPTIONS)
+
+HWOBJ = reg_orion.o reg_pear.o reg_pseudo.o
+APPOBJ = storage.o network.o crc16.o genlib.o xdsub.o
+EGGOBJ = egg.o eggui.o lecuyer.o usleep.o
+BASKETOBJ = basket.o
+TESTOBJ = testmain.o
+
+PROGRAMS = eggsh basket regtest
+
+default: $(PROGRAMS)
+
+all: $(PROGRAMS) tarballs
+
+eggsh: $(EGGOBJ) $(HWOBJ) $(APPOBJ)
+       $(CC) $(CFLAGS) -o eggsh $(EGGOBJ) $(HWOBJ) $(APPOBJ) $(LIBS)
+
+examine: $(APPOBJ) examine.o
+       $(CC) $(CFLAGS) -o examine storage.o crc16.o genlib.o examine.o
+
+basket: $(BASKETOBJ) $(APPOBJ)
+       $(CC) $(CFLAGS) -o basket $(BASKETOBJ) $(APPOBJ) $(LIBS)
+
+tarballs: $(PROGRAMS)
+       tar czf eggware.tgz sample.eggrc eggsh regtest README
+       tar czf eggsrc.tgz  sample.eggrc sample.basketrc $(PROGRAMS) README Makefile *.c *.h
+
+test:  eggsh
+       eggsh 1 9600 200 10 10
+
+clean:
+       rm -f $(PROGRAMS) *.o *.bak .*.bak core dumpreg.dat egg.status eggsample.pid
+
+cleandata:
+       rm -rf 199*-*
+
+basket.o: basket.c global.h genlib.h storage.h network.h version.h
+
+#collect.o: collect.c global.h genlib.h regs.h collect.h
+
+egg.o: egg.c global.h genlib.h storage.h network.h regs.h version.h
+
+eggui.o: eggui.c global.h genlib.h errnos.h eggui.h regs.h version.h
+
+global.h: byteorder.h
+
+hw_pear.o: hw_pear.c global.h genlib.h
+
+lecuyer.o: lecuyer.c
+
+network.o: network.c global.h genlib.h network.h
+
+regs.h: reg.h
+
+reg_orion.o: reg_orion.c reg.h
+
+reg_pear.o: reg_pear.c reg.h
+
+reg_pseudo.o: reg_pseudo.c reg.h lecuyer.h
+
+storage.o: storage.c global.h genlib.h storage.h
+
+usleep.o: usleep.c
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..b2422ce
--- /dev/null
+++ b/README
@@ -0,0 +1,39 @@
+                     GCP (EGG) Software README
+
+    Setting up to host an egg for the Global Consciousness Project
+
+The experiment produces data and stores them on your local disk as well
+as shipping the data to the central server.  You will want to create a
+separate directory for the egg material.  If you have a computer sent
+from the GCP, the directory will be /home/egg. The software will create
+within that directory subdirectories to store the data.  The storage
+load is not large (see the architecture document on the website,
+http://noosphere.princeton.edu, for more detail.  After some months 
+you may wish to remove old data, but leave two or more months' data for
+backup.  
+
+The program you need to run is eggsh.  It uses a configuration file
+called .eggrc, and you must edit the line near the end to enter your IP
+address.  This should be a fixed IP address if possible, but the basket
+software can accept data from eggs at dynamic addresses.  A file called 
+sample.eggrc or eggrc.mod can be renamed to .eggrc if the .eggrc is not
+present.  The "joe" editor is relatively user-friendly.
+
+If the program does not run, you may need to recompile on your machine.
+There is a makefile.  Do make clean, then make.
+
+The sample .eggrc file specifies the first serial port, and if you are 
+using a different port, you must change that specification.
+
+The sample pppscript file assumes you will have a fixed IP.  You will
+have more appropriate pppscript and pppchat models at the time you are
+setting up.
+
+Let us know about ways to make this process simple and efficient.  Write
+down your experiences and difficulties, and send to us any useful notes 
+that can be part of the instructions for other hosts.
+
+Thanks
+
+RDN, last update: 99-03-14
+
diff --git a/basket.c b/basket.c
new file mode 100644 (file)
index 0000000..abd33c6
--- /dev/null
+++ b/basket.c
@@ -0,0 +1,837 @@
+/* PROGRAM:    basket
+ * FILE:       $Header: /home/egg/src/RCS/basket.c,v 1.8 1999/02/28 19:58:49 ghn Exp $
+ * PURPOSE:    Main program for basket software
+ * AUTHOR:     Greg Nelson
+ * DATE:       98-06-28
+ *
+ * REVISED:
+ * $Log: basket.c,v $
+ * Revision 1.8  1999/02/28 19:58:49  ghn
+ * Version 5.1: Support for ip address masks to allow us to limit egg
+ * spoofing to users on a limited subnet (cf. EGG_DYNAMIC).  Added
+ * flexible interface/port cmdline arguments and config file arguments.
+ * Report the eggs we are accepting.
+ *
+ * Revision 1.7  1999/01/01 23:52:31  ghn
+ * Allow basket to start with no net connection and handle connection
+ * going up and down.
+ *
+ * Revision 1.6  1998/12/31 22:07:56  ghn
+ * Rev 5 code: includes multi-reg support, HTML, etc.
+ *
+ * Revision 1.5  1998/08/03 20:40:13  kelvin
+ * PACKETDUMP, SIGHUP reset when signal caught.
+ * 
+ * Revision 1.4  1998/08/01  18:52:49  ghn
+ * Added John's byte-order-independence changes.
+ *
+ * Revision 1.3  1998/08/01 17:02:05  ghn
+ * Incorporate John's Solaris fixes and terminate STAT lines properly.
+ *
+ * Revision 1.2  1998/07/21 11:44:04  ghn
+ * Added headers.
+ *
+ * Copyright 1998 - Greg Nelson
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include "global.h"
+#include "genlib.h"
+#include "storage.h"
+#include "network.h"
+#include "errnos.h"
+#include "version.h"
+
+#define EGGSTATS        "egg.status"
+
+static int32 BasketReceiveDataPacket(char *pktbuf, int16 isegg, int16 thisegg,
+                             struct sockaddr_in *rhost);
+static void MakeRequest(ReqPacket *pkt, uint16 eggid, uint32 whence);
+static void MakeSettings(SettingsPacket *pkt, uint16 eggid);
+static void LoadRCFile(void);
+static void LoadEggStats(void);
+static void SaveEggStats(void);
+static void handle_sighup(int arg);
+
+EggEntry eggtable[MAX_EGGS];
+BasketEntry baskettable[MAX_BASKETS];
+EggHeader protocol;
+
+short numeggs, numbaskets;
+char   *pgmname;                     /* Program name from argv[0] */
+char   *myaddr;                      /* Interface to bind */
+int16  myport;                       /* Service port to bind */
+
+static int htmlInterval = 0;         /* HTML status file update interval in seconds */
+static char htmlFile[256] = "";       /* HTML status file name */
+static uint32 upsince = 0;           /* Basket start time */
+#define htmlReport (htmlFile[0] != 0) /* Make HTML report ? */
+
+struct {
+    uint32 firstPacket;               /* Time of "first contact" with egg */
+    uint32 trials;                   /* Number of trials received */
+    uint32 missing;                  /* Number of missing samples */
+    double sum;                      /* Sum of trials to date */
+} eggStatistics[MAX_EGGS];
+
+/*  updateHTML --  Update HTML status file if a decent interval
+                   has passed since the last update.  */
+
+static void updateHTML(void)
+{
+    if (htmlReport) {
+       int i;
+       static uint32 lastHtml = 0;
+       uint32 now = getzulutime(NULL);
+       char udate[256], ustime[256];
+        static char timeFormat[] = "%Y-%m-%d %T";
+
+       /* When first called, set the last HTML update interval to
+          the present moment.  This defers generation of the first
+          HTML report until htmlInterval elapses.  This prevents
+          issuing a potentially-misleading HTML report based on
+          incomplete information. */
+
+       if (lastHtml == 0) {
+           lastHtml = now;
+       }
+
+       if ((now - lastHtml) >= htmlInterval) {
+           FILE *hf;
+
+           lastHtml = now;
+            hf = fopen(htmlFile, "w");
+           if (hf == NULL) {
+                fprintf(stderr, "Cannot open HTML status file %s.  Updates discarded.\n", htmlFile);
+               htmlFile[0] = 0;      /* Suppress further updates */
+               return;
+           }
+
+           strftime(udate, sizeof udate, timeFormat, gmtime((time_t *) &now));
+           strftime(ustime, sizeof ustime, timeFormat, gmtime((time_t *) &upsince));
+
+            fprintf(hf, "\
+<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n\
+<html version=\"-//W3C//DTD HTML 3.2 Final//EN\">\n\
+<head>\n\
+<title>Basket %s Status Report</title>\n\
+<meta http-equiv=\"Refresh\" content=\"%d\">\n\
+</head>\n\
+<body>\n\
+<center>\n\
+<h1>Basket %s Status Report</h1>\n\
+<h3>Last update: %s UTC</h3>\n\
+<h4>Basket started at %s UTC<br>\n\
+%s</h4>\n\
+</center>\n\
+<center>\n\
+<h2>Egg Status</h2>\n\
+<table border cellpadding=4>\n\
+<tr><th>Name<th>Number<th>Up Since<th>Last Packet<th>Active\n\
+<th>Trials<th>Missed<th>Coverage<th>Mean\n\
+",
+           baskettable[0].name, htmlInterval, baskettable[0].name, udate, ustime, Version);
+
+           for (i = 0; i < numeggs; i++) {
+             char url1[256], url2[256];
+
+             if ((eggtable[i].lastupd == 0) || (eggStatistics[i].firstPacket == 0)) {
+                  strcpy(udate, "<td colspan=2 align=center><em>Never contacted</em>");
+                  strcpy(ustime, "");
+             } else {
+                  strftime(udate, sizeof udate, "<td align=center>%Y-%m-%d %T", gmtime((time_t *) &eggtable[i].lastupd));
+                  strftime(ustime, sizeof ustime, "<td align=center>%Y-%m-%d %T", gmtime((time_t *) &eggStatistics[i].firstPacket));
+             }
+             if (eggtable[i].url == NULL) {
+                 url1[0] = url2[0] = 0;
+             } else {
+                  sprintf(url1, "<a href=\"%s\">", eggtable[i].url);
+                  strcpy(url2, "</a>");
+             }
+              fprintf(hf, "<tr><td>%s%s%s<td align=center>%d%s%s<td align=center>%s\n\
+",
+                     url1,
+                     eggtable[i].name,
+                     url2,
+                     eggtable[i].id,
+                     ustime,
+                     udate,
+                      eggtable[i].setup ? "Yes" : "No"
+             );
+
+             if (eggStatistics[i].trials == 0) {
+                  fprintf(hf, "<td align=center>&nbsp;<td align=center>&nbsp;<td align=center>&nbsp;<td align=center>&nbsp;\n");
+             } else {
+                 uint32 coverage;
+
+                  /* The following expression computes "coverage" for
+                    the egg--the percentage of samples received
+                    to the number prescribed by the protocol
+                    for the time in which the egg has been in contact. */
+                 coverage = (100L * eggStatistics[i].trials * protocol.samp_rec) /
+                     (protocol.sec_rec * (now - eggStatistics[i].firstPacket));
+                 if (coverage > 100) {
+                     coverage = 100;
+                 }
+
+                  fprintf(hf, "<td align=center>%ld<td align=center>%ld<td align=center>%ld%%<td align=center>%.3f\n",
+                         eggStatistics[i].trials,
+                         eggStatistics[i].missing,
+                         coverage,
+                         eggStatistics[i].sum / eggStatistics[i].trials
+                 );
+             }
+           }
+
+
+            fprintf(hf, "\
+</table>\n\
+</center>\n\
+<center>\n\
+<h4>Egg Status Table Fields</h4>\n\
+<table width=\"75%%\" cellpadding=3>\n\
+<tr><td valign=top><b>Name</b> <td>Egg name<p>\n\
+<tr><td valign=top><b>Number</b> <td>Egg number.  Thousands digit indicates type of random event generator.<p>\n\
+<tr><td valign=top><b>Up Since</b> <td>Time and date in first packet received from egg since basket started.<p>\n\
+<tr><td valign=top><b>Last Packet</b> <td>Time and date of most recently received packet from egg.<p>\n\
+<tr><td valign=top><b>Active</b> <td>Has egg ever communicated with this basket since it was started?<p>\n\
+<tr><td valign=top><b>Trials</b> <td>Number of trials received from this egg.<p>\n\
+<tr><td valign=top><b>Missed</b> <td>Number of scheduled trials missed by egg due to synchronisation problems.<p>\n\
+<tr><td valign=top><b>Coverage</b> <td>Percent of trials collected from this egg since basket\n\
+    started compared to the number scheduled by the protocol.<p>\n\
+<tr><td valign=top><b>Mean</b> <td>Arithmetic mean of trials performed by this egg.\n\
+</table>\n\
+</center>\n\
+</body>\n\
+</html>\n");
+           fclose(hf);
+#ifdef DEBUG
+            fprintf(stderr, "Updating HTML file %s at %s",
+               htmlFile, asctime(gmtime((time_t *) &now)));
+#endif
+       }
+    }
+}
+
+static void Usage(void) {
+  printf("Usage: %s [-i interface] [-p port]\n", pgmname);
+  exit(-1);
+}
+
+/*  Main program.  */
+
+int main(int argc, char *argv[]) {
+  char                 *pktbuf;
+  uint16               pkttype, pktsize, pkt_eggid;
+  int                  sdlisten, res;
+  struct sockaddr_in   rhost;
+  ReqPacket            rpkt;
+  SettingsPacket       spkt;
+  int                  i, isbasket, isegg, thisegg;
+  char                 *remname;
+#ifdef SIGACTION_WORKING
+  struct sigaction     sigact;
+#endif
+#ifdef EGG_DYNAMIC 
+  uint32               ipvalidate, ipmatch, ipmask;
+#endif
+
+  pgmname = argv[0];
+  argv++; argc--;
+
+  /* Defaults correspond to original usage */
+  myport = BASKETPORT;
+  myaddr = NULL;
+
+  while (argc) {
+    if (argv[0][0] == '-') {
+      switch(argv[0][1]) {
+      case 'i':        /* Specify interface */
+       if (argc < 2) Usage();
+       myaddr = argv[1];
+       argv++; argc--;
+       break;
+      case 'p':        /* Specify port */
+       if (argc < 2) Usage();
+       myport = atoi(argv[1]);
+       if (myport < 0) Usage();
+       argv++; argc--;
+       break;
+      default: /* Oops. */
+       Usage();
+      }
+    } else {
+      Usage();
+    }
+    argv++; argc--;
+  }
+
+  LoadRCFile();
+  LoadEggStats();
+
+#ifdef Solaris
+  /* Solaris has an eccentric definition of sigaction() which
+     doesn't seem to even work according to Sun's own
+     documentation.  (sa_flags is a 4 element array of
+     unsigned longs, with no mention of how one stores the
+     flags into it.)  Let's just use plain old signal for
+     the moment. */
+  signal(SIGHUP, handle_sighup);
+#else
+#ifdef SIGACTION_WORKING
+  sigact.sa_handler = handle_sighup;
+  sigact.sa_mask = 0;
+  sigact.sa_flags = 0;
+  sigact.sa_restorer = NULL;
+  sigaction(SIGHUP, &sigact, NULL);
+#else
+  signal(SIGHUP, handle_sighup);
+#endif
+#endif
+
+  /* No connection at outset */
+  sdlisten = -1;
+
+  /* Initialize storage system. */
+  InitStorage("%Y-%m/%Y-%m-%d-$b");
+
+  upsince = getzulutime(NULL);
+
+  fprintf(stderr, "Starting basket %s.\n", Version);
+
+  /* Inner loop is currently single thread:
+     1A. Wait for connection.  When a connection comes in,
+        packet has been decrypted and verified.
+     1B. Validate connection.
+     1C. Reply to connection. */
+  while (1) {
+    /* If no current listening connection, try to get one,
+       and if that fails, sleep and try later. */
+
+    if (sdlisten < 0) {
+      /* Get a listening socket at BASKETPORT.
+         Only allow our */
+      sdlisten = InitNetwork(myaddr, myport);
+      if (sdlisten < 0) {
+       sleep(1);
+       continue;
+      }
+    }
+
+    /* 1A. Wait for connection. */
+    res = NetListen(sdlisten, &pktbuf, &rhost, TRUE);
+    if (res < 0) {
+      fprintf(stderr, "NetListen error: %d\n", res);
+      continue;
+    }
+    
+    /* 1B. Validate connection. 
+       Remote host address is in rhost.  Make sure this is either an
+       egg or a basket, and set the flag appropriately. */
+#ifdef EGG_DYNAMIC
+    ipvalidate = ntohl(rhost.sin_addr.s_addr);
+#endif
+
+    isegg = isbasket = 0;
+    for (i = 0; i < numeggs; i++) {
+#ifdef EGG_DYNAMIC
+      /* When mask == 32:      ipmask = 0xFFFFFFFF (all significant)
+             mask == 16:       ipmask = 0xFFFF0000 (only top half sig)
+             mask ==  0:       ipmask = 0x00000000 (none significant) */
+      ipmask = (0xFFFFFFFF << (32-eggtable[i].netmask));
+      ipmatch = ntohl(eggtable[i].ipaddr.sin_addr.s_addr);
+      if ((ipvalidate & ipmask) == (ipmatch & ipmask)) {
+       isegg = eggtable[i].id;
+       thisegg = i;
+       remname = eggtable[i].name;
+      }      
+#else
+      if (!memcmp(&(rhost.sin_addr),
+                 &(eggtable[i].ipaddr.sin_addr),
+                 sizeof(rhost.sin_addr))) {
+       isegg = eggtable[i].id;
+       thisegg = i;
+       remname = eggtable[i].name;
+      }
+#endif
+    }
+    for (i = 0; i < numbaskets; i++) {
+      if (!memcmp(&(rhost.sin_addr),
+                 &(baskettable[i].ipaddr.sin_addr),
+                 sizeof(rhost.sin_addr))) {
+       isbasket = 1;
+       remname = baskettable[i].name;
+      }
+    }
+
+    if (!isegg && !isbasket) {
+      fprintf(stderr, "Attempt to connect from unknown source: %s\n",
+             sockaddr2dquad(&rhost));
+      continue;
+    }
+
+    memcpy(&pkttype, pktbuf, sizeof(uint16));
+    memcpy(&pktsize, pktbuf+sizeof(uint16), sizeof(uint16));
+    pkttype = ntohs(pkttype);
+    pktsize = ntohs(pktsize);
+    if (pktsize < 6) {
+      fprintf(stderr, "Packet doesn't contain an egg id, why?\n");
+      continue;
+    }
+    pkt_eggid = ntohs(*((uint16 *) (pktbuf + 4)));
+
+    /* Am I hearing from who I think I am? */
+#if EGG_DYNAMIC
+    /* Have to assume the best case before testing: that the
+       pkt_eggid is actually correct. */
+    for (i = 0; i < numeggs; i++) if (eggtable[i].id == pkt_eggid) break;
+
+    if (i == numeggs) {
+      /* Must be a spoof, and possibly an attempt to crash software too. */
+      fprintf(stderr, "%s: Egg spoofing?  %s reporting itself as %d\n",
+             pgmname, hl2dquad(ipvalidate), pkt_eggid);
+      continue;
+    }
+
+    ipmask = (0xFFFFFFFF << (32-eggtable[i].netmask));
+    ipmatch = ntohl(eggtable[i].ipaddr.sin_addr.s_addr);
+    if ((ipvalidate & ipmask) != (ipmatch & ipmask)) {
+      fprintf(stderr, "%s: Egg spoofing?  %s reporting itself as %d\n",
+             pgmname, hl2dquad(ipvalidate), pkt_eggid);
+      continue;
+    } else {
+      isegg = eggtable[i].id;
+      thisegg = i;
+      remname = eggtable[i].name;
+    }      
+#else
+    if (isegg != pkt_eggid) {
+      fprintf(stderr, "%s: Egg spoofing?  %d reporting itself as %d\n",
+             pgmname, isegg, pkt_eggid);
+      continue;
+    }
+#endif
+
+    /* 1D. Reply to connection. */
+    switch(pkttype) {
+    case DATA_PACKET:
+      /* Put it away. */
+      BasketReceiveDataPacket(pktbuf, isegg, thisegg, &rhost);
+
+      break;
+    case REQ_PACKET:
+      /* Is this how baskets communicate? */
+      break;
+    case AWAKE_PACKET:
+#ifdef PACKETDUMP
+      {
+       uint32 egg_now_time;
+
+       memcpy(&egg_now_time, pktbuf + 6, sizeof egg_now_time);
+       egg_now_time = ntohl(egg_now_time);
+       fprintf(stderr, "Awake egg %d (%s) at its %lu: %s",
+               pkt_eggid, eggtable[thisegg].name, egg_now_time,
+               asctime(gmtime((time_t *) &egg_now_time)));
+      }
+#endif
+
+      /* Calculate last data received from this egg, decide what
+        to ask for. */
+      MakeRequest(&rpkt, isegg, eggtable[thisegg].lastupd);
+      rhost.sin_port = htons(EGGPORT);
+      res = NetTalk(&rhost, (char *)&rpkt, TRUE);
+      if (res < 0) fprintf(stderr, "NetTalk error %d.\n", res);
+
+      /* If setup looks like it's out of date, correct it. */
+      if (!eggtable[thisegg].setup) {
+       MakeSettings(&spkt, thisegg);
+       rhost.sin_port = htons(EGGPORT);
+       res = NetTalk(&rhost, (char *)&spkt, TRUE);
+        if (res < 0) fprintf(stderr, "NetTalk error %d.\n", res);
+      }
+      break;
+
+    case SETTINGS_PACKET:
+      /* I don't have settings, Bozo. */
+      break;
+    }
+    free(pktbuf);
+  }
+}
+
+/*  handle_sighup  --  Catch SIGHUP and reload configuraton files.  */
+
+static void handle_sighup(int arg) {
+  /* Reread the RC file */
+  LoadRCFile();
+  LoadEggStats();
+#ifdef DEBUG
+  fprintf(stderr, "Received SIGHUP: reloading configuration files.\n");
+#endif
+#ifndef SIGACTION_WORKING
+  signal(SIGHUP, handle_sighup);      /* Reset signal handler for bottom-feeder signal() */
+#endif
+}
+
+/*  LoadRCFile --  Load basket configuration file.  */
+
+static void LoadRCFile(void) {
+  FILE *fp;
+  char linebuf[200];
+  char *myargv[MAX_PARSE], *tp;
+  int myargc, lcount, p, i;
+
+  if ((fp = fopen(".basketrc", "r")) == NULL) {
+    if ((fp = fopen("~/.basketrc", "r")) == NULL) {
+      if ((fp = fopen("/etc/basketrc", "r")) == NULL) {
+        fprintf(stderr, "%s: Couldn't find a basket RC file.\n", pgmname);
+       exit(-1);
+      }
+    }
+  }
+
+  numeggs = numbaskets = 0;
+  lcount = 0;
+
+  while(fgets(linebuf, 200, fp) != NULL) {
+    /* Comments and blank lines ignored. */
+    lcount++;
+    if (*linebuf == '#' || *linebuf == '\n') continue;
+    Parse(linebuf, &myargc, myargv);
+    if (myargc == 0) continue;
+    if (!strcmp(myargv[0], "EGG")) {
+      if (myargc < 7 || myargc > 8) {
+        fprintf(stderr, "%s: RC error, line %d, poor %s format\n", 
+               pgmname, lcount, myargv[0]);
+       exit(-1);
+      }
+       
+      eggtable[numeggs].name = mallocpy(myargv[1]);
+      eggtable[numeggs].id = atoi(myargv[2]);
+      dquad2sockaddr(&(eggtable[numeggs].ipaddr), 
+                    &(eggtable[numeggs].netmask),
+                    myargv[3]);
+      eggtable[numeggs].primbasket = mallocpy(myargv[4]);
+      eggtable[numeggs].conntype = (!strcmp(myargv[5], "PERM"))?CONN_PERM:CONN_DND;
+      eggtable[numeggs].connival = atoi(myargv[6]);
+      eggtable[numeggs].url = NULL;
+      if ((myargc > 7) && (strcmp(myargv[7], ".") != 0)) {
+       eggtable[numeggs].url = mallocpy(myargv[7]);
+      }
+      numeggs++;
+    } else if (!strcmp(myargv[0], "BASKET")) {
+      if (myargc != 3) {
+        fprintf(stderr, "%s: RC error, line %d, poor %s format\n", 
+               pgmname, lcount, myargv[0]);
+       exit(-1);
+      }
+      baskettable[numbaskets].name = mallocpy(myargv[1]);
+      dquad2sockaddr(&(baskettable[numbaskets].ipaddr), NULL, myargv[2]);
+      numbaskets++;
+    } else if (!strcmp(myargv[0], "PROTOCOL")) {
+      if (myargc != 5) {
+        fprintf(stderr, "%s: RC error, line %d, poor %s format\n", 
+               pgmname, lcount, myargv[0]);
+       exit(-1);
+      }
+      protocol.samp_rec = atoi(myargv[1]);
+      protocol.sec_rec = atoi(myargv[2]);
+      protocol.rec_pkt = atoi(myargv[3]);
+      protocol.trialsz = atoi(myargv[4]);
+    } else if (!strcmp(myargv[0], "HTML")) {
+      if (myargc != 3) {
+        fprintf(stderr, "%s: RC error, line %d, poor %s format\n", 
+               pgmname, lcount, myargv[0]);
+       exit(-1);
+      }
+      htmlInterval = atoi(myargv[1]);
+      strcpy(htmlFile, myargv[2]);
+#ifdef DEBUG
+      fprintf(stderr, "Updating HTML file %s every %d seconds.\n", htmlFile, htmlInterval);
+#endif
+    } else if (!strcmp(myargv[0], "PORT") ||
+              !strcmp(myargv[0], "INTERFACE")) {
+      if (myargc != 2) {
+        fprintf(stderr, "%s: RC error, line %d, poor %s format\n", 
+               pgmname, lcount, myargv[0]);
+       exit(-1);
+      }
+      tp = (char *) malloc(200);
+      for (*tp = 0, p = 1; p < myargc; p++) {
+       strcat(tp, myargv[p]);
+        if (p < myargc-1) strcat(tp, " ");
+      }
+
+      if (!strcmp(myargv[0], "PORT")) {
+       if (atoi(tp) <= 0) {
+         fprintf(stderr, "%s: RC error, ignoring bad port %d, using %d\n",
+                 pgmname, atoi(tp), myport);
+       } else {
+         myport = atoi(tp);
+       }
+       free(tp);
+      } else if (!strcmp(myargv[0], "INTERFACE")) myaddr = tp;
+    } else {
+      fprintf(stderr, "%s: RC error, %s is unknown keyword\n", pgmname, myargv[0]);
+      exit(-1);
+    }
+  }
+  fclose(fp);
+
+#if REPORT > 0
+  /* Report a list of the eggs that we are accepting. */
+  for (i = 0; i < numeggs; i++) {
+    fprintf(stderr, "Egg %2d:\tid = %4d\tip=%s/%d\n", 
+           i, eggtable[i].id, 
+           sockaddr2dquad(&(eggtable[i].ipaddr)),
+           eggtable[i].netmask);
+  }
+#endif
+}
+
+/*  BasketReceiveDataPacket  --  Process a data packet received frm an egg.  */
+
+static int32 BasketReceiveDataPacket(char *pkt, int16 isegg, int16 thisegg,
+                             struct sockaddr_in *rhost) {
+  EggHeader            *dpktp;
+  EggCarton            result;
+  ReqPacket            rpkt;
+  uint16               offset, rec;
+  uint32               latest;
+  int32                res;
+#ifdef DEBUG
+  uint16               i;
+#endif
+
+  EggHeader dpk;
+  char *pktP = pkt;
+  
+  /* Unpack the portable header into a host-order and aligned
+     EggHeader packet. */
+  
+  dpktp = &dpk;
+  unpackShort(dpk.type);
+  unpackShort(dpk.pktsize);
+  unpackShort(dpk.eggid);
+  unpackShort(dpk.samp_rec);
+  unpackShort(dpk.sec_rec);
+  unpackShort(dpk.rec_pkt);
+  unpackByte(dpk.trialsz);
+  unpackShort(dpk.numrec);
+
+  /* This spoofing test is probably completely redundant now. */
+#ifndef EGG_DYNAMIC      
+  /* Am I hearing from an egg, and is it who I think it is? */
+  if (isegg && isegg != dpktp->eggid) {
+    fprintf(stderr, "%s: Egg spoofing?  %d reporting itself as %d\n",
+           pgmname, isegg, dpktp->eggid);
+    return -1;
+  }
+#endif
+
+  /* Save the packet header in the result EggCarton buffer. */
+
+  memcpy(&(result.hdr), dpktp, sizeof(EggHeader));
+  offset = sizeof(EggHeader);
+
+#ifdef DEBUG
+  {
+    uint32 stamp = 0;
+    char *spktp = pktP;
+
+    if (result.hdr.numrec > 0) {
+      unpackLong(stamp);
+    }
+    pktP = spktp;
+    fprintf(stderr, "Received packet: %d records from egg %d (%s).\n",
+      result.hdr.numrec, dpktp->eggid, eggtable[thisegg].name);
+  }
+#endif
+
+  /* Unpack the individual data records from the packet and
+     transcribe to the result EggCarton.  */
+  
+  for (latest = 0, rec = 0; rec < result.hdr.numrec; rec++) {
+    unpackLong(result.records[rec].timestamp);  /* Record timestamp */
+    if (result.records[rec].timestamp > latest)
+      latest = result.records[rec].timestamp;
+#ifdef DEBUG
+    fprintf(stderr, "  %10lu  ", result.records[rec].timestamp);
+#endif
+    /* Assumes sizeof(trial) = 1 */
+    unpackBytes(&(result.records[rec].trials), result.hdr.samp_rec);
+#ifdef DEBUG
+    for (i = 0; i < result.hdr.samp_rec; i++) {
+      fprintf(stderr, "%3d ", result.records[rec].trials[i]);
+    }
+    fprintf(stderr, "%s", asctime(gmtime((time_t *) &result.records[rec].timestamp)));
+#endif
+  }
+
+  /* Save the packet in the basket database file. */
+  
+  if ((res = SavePacket(&result)) < 0) return res;
+
+  /* If we're generating an HTML report, update the per-basket
+     statistics based on the content of this packet. */
+
+  if (htmlReport) {
+      int r, t;
+      uint32 ptrials = 0, psum = 0, dropped = 0;
+
+      /* Update the egg status for HTML report. */
+
+      for (r = 0; r < result.hdr.rec_pkt; r++) {
+         for (t = 0; t < result.hdr.samp_rec; t++) {
+             if (result.records[r].trials[t] != EGG_MISSING_DATA) {
+                 ptrials++;
+                 psum += result.records[r].trials[t];
+             } else {
+                 dropped++;
+             }
+         }
+      }
+      eggStatistics[thisegg].trials += ptrials;
+      eggStatistics[thisegg].sum += psum;
+
+      /* If this is the first packet received from the egg, and
+         the first sample in it is missing, don't count missing
+         samples against this egg, since it's probable this is
+        the first packet collected since the egg started, which
+        will contain initial missing samples for time before the
+        egg started. */
+
+      if (eggStatistics[thisegg].firstPacket != 0) {
+         eggStatistics[thisegg].missing += dropped;
+      } else {
+         eggStatistics[thisegg].firstPacket = result.records[0].timestamp;
+      }
+  }
+
+  if (latest > eggtable[thisegg].lastupd) {
+    eggtable[thisegg].lastupd = latest;
+    SaveEggStats();
+    updateHTML();                    /* Update HTML status file, if warranted */
+    if (latest < getzulutime(NULL)) {
+      /* Get more data if it looks like there should be more. */
+      MakeRequest(&rpkt, isegg, eggtable[thisegg].lastupd);
+      rhost->sin_port = htons(EGGPORT);
+      res = NetTalk(rhost, (char *)&rpkt, TRUE);
+      if (res < 0) fprintf(stderr, "NetTalk error %d.\n", (int)res);
+    }
+  }
+
+  /* If the protocol now in effect differs from that of the
+     packet just received from the egg, mark the egg as not
+     set-up, and thus in need of a SETTINGS_PACKET to reset
+     its protocol. */
+
+  eggtable[thisegg].setup = (protocol.samp_rec == dpktp->samp_rec &&
+                            protocol.sec_rec == dpktp->sec_rec &&
+                            protocol.rec_pkt == dpktp->rec_pkt &&
+                            protocol.trialsz == dpktp->trialsz);
+
+  return ERR_NONE;
+}
+
+/*  MakeRequest  --  Assemble a request packet for a given egg,
+                    specifying, in whence, the time and date
+                    of the last sample received from that egg.  */
+
+static void MakeRequest(ReqPacket *pkt, uint16 eggid, uint32 whence) {
+  char *pktP = (char *) pkt;
+
+  packShort(REQ_PACKET);
+  packShort((4 * sizeof(uint16)) + sizeof(uint32));
+  packShort(eggid);
+  packLong(whence);
+}
+
+/*  MakeSettings  --  Create a settings packet.  This packet,
+                     sent periodically to connected eggs,
+                     informs them of any change in protocol.  */
+
+static void MakeSettings(SettingsPacket *pkt, uint16 eggid) {
+  char *pktP = (char *) pkt;
+
+  packShort(SETTINGS_PACKET);
+  packShort((7 * sizeof(uint16)) + sizeof(uint32) + sizeof(trial));
+  packShort(eggid);
+  packLong(getzulutime(NULL));
+  packShort(protocol.samp_rec);
+  packShort(protocol.sec_rec);
+  packShort(protocol.rec_pkt);
+  packByte(protocol.trialsz);
+}
+
+/*  LoadEggStats  --  Initialise in-memory egg status table
+                     from the EGGSTATS file.  */
+
+static void LoadEggStats(void) {
+  FILE *fp;
+  char linebuf[200], *myargv[MAX_PARSE], *name;
+  int i, f, myargc, id, setup;
+  int32 lastupd;
+
+  /* Default stats */
+  for (i = 0; i < numeggs; i++) {
+    eggtable[i].lastupd = 0;
+    eggtable[i].setup = 0;
+  }
+  
+  if ((fp = fopen(EGGSTATS, "r")) == NULL) return;
+  
+  while(fgets(linebuf, 200, fp) != NULL) {
+    /* Comments and blank lines ignored. */
+    if (*linebuf == '#' || *linebuf == '\n') continue;
+    Parse(linebuf, &myargc, myargv);
+    if (myargc == 0) continue;
+    if (!strcmp(myargv[0], "STAT") && myargc == 5) {
+      name = myargv[1];
+      id = atoi(myargv[2]);
+      lastupd = atol(myargv[3]);
+      setup = atoi(myargv[4]);
+      for (f = 0, i = 0; i < numeggs; i++) {
+       if (eggtable[i].id == id &&
+           !strcmp(eggtable[i].name, name)) {
+         eggtable[i].lastupd = lastupd;
+         eggtable[i].setup = setup;
+         f = 1;
+       }
+      }
+      if (!f) fprintf(stderr, "%s: Egg '%s' no longer hosted.\n", pgmname, name);
+    }
+  }
+  fclose(fp);
+}
+
+/*  SaveEggStats  --  Dump in-memory egg status to the EGGSTATS file.  */
+
+static void SaveEggStats(void) {
+  FILE *fp;
+  int i;
+
+  if ((fp = fopen(EGGSTATS, "w")) == NULL) {
+    /* Couldn't write stats! */  
+    fprintf(stderr, "%s: Couldn't save egg stats!\n", pgmname);
+    return;
+  }
+
+  fprintf(fp, "# Status lines of form:\n");
+  fprintf(fp, "#   STAT <eggname> <eggid> <lastupdzulu> <setupvalid>\n");
+  for (i = 0; i < numeggs; i++) {
+    fprintf(fp, "STAT %s %d %ld %d\n",
+           eggtable[i].name,
+           eggtable[i].id,
+           eggtable[i].lastupd,
+           eggtable[i].setup);
+  }
+  fclose(fp);
+}
diff --git a/basketrc.sample b/basketrc.sample
new file mode 100644 (file)
index 0000000..09da1e6
--- /dev/null
@@ -0,0 +1,10 @@
+#BASKET diesse 193.8.230.134
+#BASKET jura 193.8.230.130
+BASKET noosphere 128.112.35.133
+EGG diesse 37 193.8.230.134 diesse PERM 1 http://www.fourmilab.ch/
+EGG noosphere 28 128.112.35.133 noosphere PERM 1 http://noosphere.princeton.edu/
+EGG throop 1003 193.8.230.132 throop PERM 1
+EGG jura 1004 193.8.230.130 jura PERM 1
+EGG dicklnx.psy.uva.nl 1000 145.18.117.41 diesse PERM 1 http://www.psy.uva.nl/bierman
+PROTOCOL 10 10 6 200
+HTML 60 /ftp/entrenous/k/basket.html
diff --git a/byteorder.h b/byteorder.h
new file mode 100644 (file)
index 0000000..6071e53
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+
+       Byte-order invariant definitions
+
+*/
+
+/*  The following macros allow packing fields of various types
+    into a byte-oriented packet with a running pointer named
+    pktP.  The reason we store then memcpy rather than storing
+    through a cast of pktP to the desired type is that the
+    latter approach can result in alignment faults when run
+    on RISC machines which typically require values to be
+    aligned at an even multiple of their length.  */
+
+#define packShort(x) { \
+                       short s = htons((short) (x)); \
+                       memcpy(pktP, &s, sizeof(short)); \
+                       pktP += sizeof(short); \
+                    }
+
+#define packLong(x) { \
+                       long l = htonl((long) (x)); \
+                       memcpy(pktP, &l, sizeof(long)); \
+                       pktP += sizeof(long); \
+                   }
+
+#define packByte(x) { \
+                       char c = (x); \
+                       memcpy(pktP, &c, sizeof(char)); \
+                       pktP += sizeof(char); \
+                   }
+
+#define packBytes(x, n) { \
+                           memcpy(pktP, x, n); \
+                           pktP += n; \
+                       }
+
+/*  These macros unpack fields of various lengths from a
+    byte-packet packet in network byte order pointed to by
+    pktP.  Note that the argument of these macros must be
+    an lvalue. */
+
+#define unpackShort(x) { \
+                           short s; \
+                           memcpy(&s, pktP, sizeof(short)); \
+                           pktP += sizeof(short); \
+                           x = ntohs(s); \
+                      }
+
+#define unpackLong(x) { \
+                          long l; \
+                          memcpy(&l, pktP, sizeof(long)); \
+                          pktP += sizeof(long); \
+                          x = ntohl(l); \
+                     }
+
+#define unpackByte(x) { \
+                       *((char *) &x) = (char) *pktP++; \
+                     }
+
+#define unpackBytes(x, n) { \
+                           memcpy(x, pktP, n); \
+                           pktP += n; \
+                         }
diff --git a/crc16.c b/crc16.c
new file mode 100644 (file)
index 0000000..fb4649f
--- /dev/null
+++ b/crc16.c
@@ -0,0 +1,68 @@
+/* PROGRAM:     eggsh
+ * FILE:        $Header: /home/egg/src/RCS/crc16.c,v 1.1 1998/07/21 11:42:15 ghn Exp $
+ * PURPOSE:     Functions to calculate CRC checksum
+ * AUTHOR:      Greg Nelson
+ * DATE:        98-04-12
+ *
+ * REVISED:     $Log: crc16.c,v $
+ * REVISED:     Revision 1.1  1998/07/21 11:42:15  ghn
+ * REVISED:     Initial revision
+ * REVISED:
+ * Copyright 1998 - Greg Nelson
+ * Redistributable under the terms of the GNU Public Licence (GPL)
+ */
+
+#include "global.h"
+
+/* Calculate 16 bit CRC checksum on specified block */
+
+#define TBLSIZE                256
+#define CRC16_POLY     0xA001
+#define CRC16_INIT     0
+
+uint16 CRC16Table[TBLSIZE] = {
+  0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
+  0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
+  0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
+  0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
+  0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
+  0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
+  0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
+  0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
+  0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
+  0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
+  0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
+  0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
+  0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
+  0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
+  0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
+  0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
+  0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
+  0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
+  0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
+  0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
+  0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
+  0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
+  0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
+  0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
+  0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
+  0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
+  0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
+  0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
+  0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
+  0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
+  0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
+  0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040};
+  
+uint16 BlockCRC16(byte *block, uint32 len) {
+  uint16 crc;
+  uint32 i, indx;
+
+  crc = CRC16_INIT;
+  for (i = 0; i < len; i++) {
+    indx = (uint8)(*block++ ^ crc);
+    crc >>= 8;
+    crc ^= CRC16Table[indx];
+  }
+  return crc;
+}
diff --git a/egg.c b/egg.c
new file mode 100644 (file)
index 0000000..84786c5
--- /dev/null
+++ b/egg.c
@@ -0,0 +1,996 @@
+/* PROGRAM:    eggsh
+ * FILE:       $Header: /home/egg/src/RCS/egg.c,v 1.8 1999/02/28 20:01:43 ghn Exp $
+ * PURPOSE:    EGG site data collection
+ * AUTHOR:     Greg Nelson
+ * DATE:       98-06-20
+ *
+ * REVISED:
+ * $Log: egg.c,v $
+ * Revision 1.8  1999/02/28 20:01:43  ghn
+ * Version 5.1: Added command line parsing for interface/port configuration.
+ * Changed default interface to 0.0.0.0, so that egg will normally bind to
+ * all incoming interfaces.  Reports interface/port it is using, right before
+ * the screen gets cleared and you can't see it.
+ *
+ * Revision 1.7  1999/01/01 23:57:20  ghn
+ * Remove excess code associated with CPU-bound version and "OLDWAY" UI
+ * and egg HW selection. Add back in updated block algorithm description.
+ * Allow non-CPU-bound version to exchange more than 1 packet per second.
+ * Get rid of warnings when network is not reachable, assuming this is a
+ * transient error.
+ *
+ * Revision 1.6  1998/12/31 22:07:56  ghn
+ * Rev 5 code: includes multi-reg support, HTML, etc.
+ *
+ * Revision 1.5  1998/08/03 20:35:55  kelvin
+ * PACKETDUMP support.
+ * 
+ * Revision 1.4  1998/08/01  21:34:26  ghn
+ * Connected user interface and added PSEUDO suppor into main line.
+ *
+ * Revision 1.3  1998/08/01 18:51:25  ghn
+ * Added John's byte-order-independence changes.
+ * 
+ * Revision 1.2  1998/08/01 17:04:26  ghn
+ * Lots of fixes from John, plus DND support.
+ * 
+ * Revision 1.1  1998/07/21 11:41:35  ghn
+ * Initial revision
+ * 
+ * Copyright 1998 - Greg Nelson
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+
+#include "global.h"
+#include "genlib.h"
+#include "storage.h"
+#include "network.h"
+#include "eggui.h"
+#include "errnos.h"
+#include "regs.h"
+#include "regtable.h"
+#include "version.h"
+
+/* Good old Linux doesn't define the following functions
+   by default in the #include files the manual page claims
+   it does, and the conditional declarations with __USE_xx
+   doesn't work.  Explicitly declare them, which doesn't
+   seem to do any harm on systems where these functions are
+   properly declared.  If you get errors on the following
+   declarations, you can probably get around them by #ifdef-ing
+   the declarations off for your platform. */
+
+extern int nice(int inc);
+extern int strcasecmp(const char *s1, const char *s2);
+
+/*  Prototypes for forward functions.  */
+
+static void MakeAwake(AwakePacket *pkt);
+static void MakeDataPkt(char **pkt, EggCarton *src);
+static void LoadRCFile(char *filename);
+static double SetCollOpts(void);
+static int GreetBasket(void);
+static void handle_sigkill(int arg), handle_sighup(int arg);
+
+EggEntry eggtable[MAX_EGGS];
+BasketEntry baskettable[MAX_BASKETS];
+EggHeader protocol;
+REG_driver *configuredREG = NULL;     /* Configured REG driver */
+
+short numeggs = 0, numbaskets = 0;
+
+static DevOpts devopts;              /* Device options for REG */
+static CollectRecord coll;
+static EggCarton savebuffer;
+char   *pgmname;                     /* Program name from argv[0] */
+char   *myaddr;                      /* Interface to bind */
+int16  myport;                       /* Service port to bind */
+int32 lastDataSent = 0;              /* Time last packet sent to basket */
+
+/* If no priority increment has been specified at compile time, set to
+   our default of Â±10. [+/- 10, for folks without 8bit editors] */
+
+#ifndef NICE
+#define NICE   10
+#endif
+static int niceness = NICE;          /* Priority increment/decrement for collection */
+
+/* Status exported to user interface. */
+
+int32 time_latency = 0, time_housekeeping = 0;
+
+#ifdef ALT_UI
+static double mean_Packet = 0.0, mean_Grand = 0.0;
+static int32 total_Packets = 0;
+#endif
+
+/*  resetCarton  --  Clear existing data from savebuffer and
+                    reinitialise for a sampling interval
+                    beginning at the specified start_time.  */
+
+static void resetCarton(uint32 start_time)
+{
+    int i;
+    uint32 sec_pkt;
+
+    /* Save current collection options to packet. */
+
+    memcpy(&(savebuffer.hdr), &(coll.opts), sizeof(EggHeader));
+
+    /* Set number of records in packet and clear record
+       buffers to missing data. */
+
+    savebuffer.hdr.numrec = savebuffer.hdr.rec_pkt; /* Mark all records present */
+
+    /* Align start_time to even multiple of seconds per
+       packet. */
+
+    sec_pkt = savebuffer.hdr.sec_rec * savebuffer.hdr.rec_pkt;
+    start_time = sec_pkt * (start_time / sec_pkt);
+
+    for (i = 0; i < savebuffer.hdr.rec_pkt; i++) {
+       savebuffer.records[i].timestamp = start_time;
+       start_time += savebuffer.hdr.sec_rec;
+#if MAXBITS < 256
+       memset(savebuffer.records[i].trials, EGG_MISSING_DATA, MAXSAMP_REC);
+#else
+#error "Can't represent MAXBITS in a byte value."
+#endif
+    }
+}
+
+/*  saveProtocol  --  Save current protocol in the .eggprotocolrc file.  */
+
+static void saveProtocol(void)
+{
+    FILE *rcfile;
+
+    rcfile = fopen(".eggprotocolrc", "w");
+    if (rcfile == NULL) {
+        fprintf(stderr, "%s: Cannot create .eggprotocolrc file.\n", pgmname);
+       exit(-1);
+    }
+    fprintf(rcfile, "#\n#  Current protocol for egg\n#\n");
+    fprintf(rcfile, "#   PROTOCOL <samp_rec> <sec_rec> <rec_pkt> <trialsz>\n");
+    fprintf(rcfile, "PROTOCOL %d %d %d %d\n", protocol.samp_rec, protocol.sec_rec,
+       protocol.rec_pkt, protocol.trialsz);
+    fclose(rcfile);
+}
+
+static void Usage(void) {
+  printf("Usage: %s [-i interface] [-p port]\n", pgmname);
+  exit(-1);
+}
+
+/*  Main program.  */
+
+int main(int argc, char *argv[]) {
+  char                 *pktbuf, *outpktbuf;
+  uint16               pkttype, pktsize;
+  int                  sdlisten, res;
+  struct sockaddr_in   rhost;
+  EggCarton            retrcart;
+  int                  i, isbasket;
+  char                 *remname;
+  int32                lastconn, lastsend;
+  double               sps;
+#ifdef SIGACTION_WORKING
+  struct sigaction     sigact;
+#endif
+  FILE *pidfile, *rcfile;
+
+  pgmname = argv[0];
+  argv++; argc--;
+
+  /* Defaults correspond to original usage */
+  myport = EGGPORT;
+  myaddr = "0.0.0.0";
+
+  while (argc) {
+    if (argv[0][0] == '-') {
+      switch(argv[0][1]) {
+      case 'i':        /* Specify interface */
+       if (argc < 2) Usage();
+       myaddr = argv[1];
+       argv++; argc--;
+       break;
+      case 'p':        /* Specify port */
+       if (argc < 2) Usage();
+       myport = atoi(argv[1]);
+       if (myport < 0) Usage();
+       argv++; argc--;
+       break;
+      default: /* Oops. */
+       Usage();
+      }
+    } else {
+      Usage();
+    }
+    argv++; argc--;
+  }
+
+#ifdef Solaris
+  /* Solaris has an eccentric definition of sigaction() which
+     doesn't seem to even work according to Sun's own
+     documentation.  (sa_flags is a 4 element array of
+     unsigned longs, with no mention of how one stores the
+     flags into it.)  Let's just use plain old signal for
+     the moment. */
+  signal(SIGKILL, handle_sigkill);
+  signal(SIGHUP, handle_sighup);
+#else
+#ifdef SIGACTION_WORKING
+  sigact.sa_handler = handle_sigkill;
+  sigact.sa_mask = 0;
+  sigact.sa_flags = 0;
+  sigact.sa_restorer = NULL;
+  sigaction(SIGKILL, &sigact, NULL);
+
+  sigact.sa_handler = handle_sighup;
+  sigaction(SIGHUP, &sigact, NULL);
+#else
+  signal(SIGKILL, handle_sigkill);
+  signal(SIGHUP, handle_sighup);
+#endif
+#endif
+
+  fprintf(stderr, "Starting egg %s.\n", Version);
+
+  /* Save our process ID in a file for folks who might wish
+     to send us a signal. */
+
+  pidfile = fopen("eggsample.pid", "w");
+  if (pidfile == NULL) {
+    fprintf(stderr, "Unable to create eggsample.pid file.\n");
+    exit(-1);
+  }
+  fprintf(pidfile, "%d\n", getpid());
+  fclose(pidfile);
+
+  /* If we aren't being run by the super-user, disable the
+     priority raising and lowering mechanism. */
+
+  if (getuid() != 0) {
+    niceness = 0;
+  }
+
+#ifdef DEBUG
+  printf("User ID = %d.  Niceness = %d.\n", getuid(), niceness);
+  printf("REG drivers installed: ");
+  for (i = 0; reg_table[i] != NULL; i++) {
+    printf(" %s", reg_table[i]->reg_name);
+  }
+  printf("\n");
+#endif
+
+  LoadRCFile(NULL);
+
+  /* If an auxiliary configuration file exists, load it on
+     top of the arguments loaded from the main RC file.  If
+     it doesn't exist, create it. */
+
+  if ((rcfile = fopen(".eggprotocolrc", "r")) != NULL) {
+    fclose(rcfile);
+    LoadRCFile(".eggprotocolrc");
+  } else {
+    saveProtocol();
+  }
+
+  if (configuredREG == NULL) {
+    fprintf(stderr, "%s: RC error, no REG specified for egg.\n",
+       pgmname);
+    exit(-1);
+  }
+#ifdef DEBUG
+    else {
+    printf("REG configured: %s = %d, %d, %ld\n", configuredREG ->reg_name,
+       devopts.type, devopts.port, devopts.baud);
+  }
+#endif
+
+
+  sps = SetCollOpts();
+
+  if ((coll.dd = OpenDev(&devopts)) < 0) {
+    fprintf(stderr, "Couldn't talk to hardware device.\n");
+    exit(1);
+  }
+
+  if (EvalSpeed(coll.dd) < coll.opts.trialsz * sps) {
+    fprintf(stderr, "Requested speed exceeds device capabilities.\n");
+    exit(-1);
+  }
+
+#if REPORT > 0
+  if (myaddr) {
+    fprintf(stderr, "TCP configured: %s.%d\n", myaddr, myport);
+  } else {
+    fprintf(stderr, "TCP configured: hostname.%d\n", myport);
+  }
+#endif
+
+  UIInit();
+
+  sdlisten = -1;       /* No conn yet. */
+  lastconn = lastsend = 0;
+
+  /* Initialise carton to zero time, indicating no data in
+     initial carton. */
+  resetCarton(0);
+
+  /* Initialize storage system */
+  InitStorage("%Y-%m/%Y-%m-%d-$06E");
+
+  /* Inner loop is currently two threads which are both processed
+     together through NBIO.
+
+     2A. Adjust priority and sleep until time computed below (2H).
+     2B. Busy wait until second ticks over.
+     2C. Empty REG input buffer (if needed), then collect sample.
+     2D. Lower priority to handle bookkeeping; save packet.
+     2E. Update user interface.
+
+     2F. Send awake packet if connival is passed.
+     2G. [DND only] Put net to sleep if we've been waiting more than a minute
+
+     1A. Wait for connection.  When a connection comes in,
+        packet has been decrypted and verified.
+     1B. Validate connection.
+     1C. Reply to connection.
+     1D. If we have more than MINSLEEP time left in second,
+         go back to 1A.
+
+     2H. Compute new sleep time.
+  */
+
+  while (1) {
+    static int sleeptime = 0;
+    static struct timeval t, lt = {0, 0};
+    struct timeval ct;
+    trial sample;
+    int havedata;
+    uint32 rindex;
+#ifdef DEBUG
+    uint32 msec, usec;
+#endif
+
+#ifndef SLACK
+#define SLACK  50000                 /* CPU loop resynchronisation time in microseconds */
+#endif
+
+#ifndef        MINSLEEP
+#define MINSLEEP       100000          /* Minimum sleep time, usec */
+#endif
+
+    /* 2A. If we have successfully computed a sleep time until
+       the window opens to collect the next sample, give up
+       the CPU until that time arrives. */
+
+    if (niceness != 0) {
+       nice(-niceness);              /* Raise priority for re-dispatch */
+    }
+    if (sleeptime > 0) {
+       Usleep((unsigned) sleeptime);
+    }
+
+    /* 2B. We're now close to the start of the next second.  Watch the
+       time until the second changes, then collect the next sample.
+       Note that this method of doing things enforces the one
+       sample/second that has become standard practise.  The code at
+       the bottom of the loop could be changed to allow for a longer
+       interval, but this code enforces a minimum of a 1-second change
+       for each sample. */
+
+    while (1) {
+      gettimeofday(&t, NULL);
+      if (lt.tv_sec == 0) lt.tv_sec = t.tv_sec;
+      if (t.tv_sec != lt.tv_sec) break;
+    }
+
+    /* 2C. It is now time to collect the next sample.  Discard any
+       any data in the input queue and get the sample. */
+
+    Discard(coll.dd);
+    sample = Sample(coll.dd, coll.opts.trialsz);
+    if (niceness != 0) {
+      nice(niceness);        /* Collection done.  Lower priority to normal. */
+    }
+
+    /* 2D. Okay, we're now in "quality time"--the slack after collection
+       of a sample until the time for the next sample arrives.
+       Now is an excellent time to do all kinds of housekeeping.
+       First of all, see if the sample just collected fits into
+       the packet currently being assembled.  If not, we need to
+       save the last packet in the egg data file and initialise
+       a new packet for the time period in which this sample was
+       collected. */
+
+    if ((savebuffer.records[0].timestamp == 0) ||
+       ((savebuffer.records[0].timestamp +
+         (savebuffer.hdr.sec_rec * savebuffer.hdr.rec_pkt)) <= t.tv_sec)) {
+
+      if (savebuffer.records[0].timestamp != 0) {
+       static int firstpacket = 1;
+#ifdef ALT_UI
+       int r, t;
+       uint32 ptrials = 0, psum = 0;
+       static uint32 totalTrials = 0;
+       static double totalGrand = 0.0;
+#endif
+
+       /* Sample isn't within current packet.  Dump packet to the egg
+          data file and reinitialise to the window containing the
+          sample we just collected.  But first, a little gimmick.  If
+          this is the first packet we've collected and it contains
+          one or more missing samples at the beginning, discard it.
+          This keeps the routine missing samples in the first packet
+          after the egg starts up from being reported as missing
+          samples due to genuine synchronisation errors. */
+
+       if (!(firstpacket && (savebuffer.records[0].trials[0] == EGG_MISSING_DATA))) {
+         SavePacket(&savebuffer);
+       }
+#ifdef DEBUG
+       else {
+         fprintf(stderr, "Dropping first packet to discard start-up missing samples.\n");
+       }
+#endif
+       firstpacket = 0;
+
+#ifdef ALT_UI
+       /* Update the last packet and grand mean data for the debug
+          interface status display. */
+
+       for (r = 0; r < savebuffer.hdr.rec_pkt; r++) {
+         for (t = 0; t < savebuffer.hdr.samp_rec; t++) {
+           if (savebuffer.records[r].trials[t] != EGG_MISSING_DATA) {
+             ptrials++;
+             psum += savebuffer.records[r].trials[t];
+           }
+         }
+       }
+       mean_Packet = ((double) psum) / ptrials;
+       totalGrand += psum;
+       totalTrials += ptrials;
+       mean_Grand = totalGrand / totalTrials;
+       total_Packets++;
+       printf("Packets sent: %ld  Packet mean: %6.2f  Grand mean: %6.2f\n",
+               total_Packets, mean_Packet, mean_Grand);
+#endif
+      }
+
+      /* Check for change to the collection parameters...
+        now is the time to implement it. */
+
+      if (coll.opts.rec_pkt != protocol.rec_pkt ||
+         coll.opts.trialsz != protocol.trialsz ||
+         coll.opts.sec_rec != protocol.sec_rec ||
+         coll.opts.samp_rec != protocol.samp_rec) {
+       sps = SetCollOpts();
+       saveProtocol();
+      }
+      /* Reinitialise carton to interval containing sample */
+      resetCarton(t.tv_sec);
+    }
+
+    /* Store the sample into the carton, which is now guaranteed to
+       bracket the interval in which it was collected. */
+
+#ifdef DEBUG
+    /* But let's be sure it really *is* in the interval. */
+    if (t.tv_sec < savebuffer.records[0].timestamp ||
+       t.tv_sec >= (savebuffer.records[0].timestamp + savebuffer.hdr.sec_rec * savebuffer.hdr.rec_pkt)) {
+      fprintf(stderr, "***Sample, collected at %ld, is not within packet starting at %ld.\n",
+             t.tv_sec, savebuffer.records[0].timestamp);
+      exit(-1);
+    }
+#endif
+
+    rindex = t.tv_sec - savebuffer.records[0].timestamp;
+    savebuffer.records[rindex / savebuffer.hdr.sec_rec].trials[rindex % savebuffer.hdr.sec_rec] = sample;
+
+    /* 2E. Update user interface. */
+
+#ifndef NO_UI
+    coll.data.trials[coll.sampct] = sample;
+    coll.sampct++;
+    UIUpdate(coll.sampct >= coll.opts.samp_rec, &coll);
+
+    if (coll.sampct >= coll.opts.samp_rec) {
+       coll.sampct = 0;
+    }
+#endif
+
+    /* Now that the current sample has been dealt which, check
+       for egg-initiated actions whose time has come.  */
+
+    /* 2F. First of all, it it's time to prod the basket with an
+       awake packet, lob one over the pole. */
+
+    if (getzulutime(NULL) - lastconn > (eggtable[0].connival * 60L)) {
+      lastsend = lastconn = getzulutime(NULL);
+      
+      /* If this is a dial-and-drop egg, connect to the network before
+        sending the packet. */
+
+      if (sdlisten < 0 && eggtable[0].conntype == CONN_DND) {
+       sdlisten = NetUp(eggtable[0].upcmd, myaddr, myport);
+      }
+      
+      GreetBasket();
+    }
+    
+    /* 2G. If this is a dial-and-drop egg and it's been more than a
+       minute since we sent anything, tear down the network connection. */
+    
+    if (eggtable[0].conntype == CONN_DND && sdlisten >= 0) {
+      if ((getzulutime(NULL) - lastsend) > 60L) {
+       NetDown(eggtable[0].dncmd, sdlisten);
+       sdlisten = -1;
+      }
+    }
+
+    /* Now deal with basket-initiated requests received on the socket
+       since the last time around the loop.  To improve throughput for
+       dialup connections, we may come back here several times if we
+       have more data and if time allows.  */
+
+    do {
+      havedata = 0;    /* Don't loop unless net activity happens. */
+
+      /* 1A. Wait for connection. */
+      if (eggtable[0].conntype == CONN_PERM) {
+       if (sdlisten < 0) {
+         /* Initialize networking, get a listening socket at EGGPORT */
+         sdlisten = InitNetwork(myaddr, myport);
+       }
+      }
+      if (sdlisten >= 0) {
+       res = NetListen(sdlisten, &pktbuf, &rhost, FALSE);
+       if (res < 0 && res != ERR_COMM_TMOUT) {
+         fprintf(stderr, "NetListen error: %d\n", res);
+         break;        /* Out of do loop */
+       }
+       /* Data present, process networking thread */
+       if (res == ERR_NONE) {
+         /* 1B. Validate connection. 
+            Remote host address is in rhost.  Make sure this is either an
+            egg or a basket, and set the flag appropriately. */
+         isbasket = 0;
+         for (i = 0; i < numbaskets; i++) {
+           if (!memcmp(&(rhost.sin_addr),
+                       &(baskettable[i].ipaddr.sin_addr),
+                       sizeof(rhost.sin_addr))) {
+             isbasket = 1;
+             remname = baskettable[i].name;
+           }
+         }
+         
+         if (!isbasket) {
+           fprintf(stderr, "Attempt to connect from unknown source: %s",
+                   sockaddr2dquad(&rhost));
+           break;      /* Out of do loop */
+         }
+
+         havedata = 1; /* Initial assumption, until disproven. */
+         
+         memcpy(&pkttype, pktbuf, sizeof(uint16));
+         memcpy(&pktsize, pktbuf+sizeof(uint16), sizeof(uint16));
+         pkttype = ntohs(pkttype);
+         pktsize = ntohs(pktsize);
+         
+         /* 1C. Reply to connection. */
+         switch(pkttype) {
+         case DATA_PACKET:
+         case AWAKE_PACKET:
+           /* Not acceptable at an eggsite. */
+           fprintf(stderr, "%s: EGG could not accept %d packet\n", 
+                   pgmname, pkttype);
+           break;
+           
+           /* Request for data from a basket. */
+           
+         case REQ_PACKET:
+           {
+             uint16 reggid;
+             uint32 stime;
+             char *pktP = pktbuf + (2 * sizeof(uint16));
+             
+             unpackShort(reggid);
+             unpackLong(stime);
+#ifdef PACKETDUMP
+             fprintf(stderr, "Request: eggid = %d, starttm = %ld: %s",
+                     reggid, stime, asctime(gmtime((time_t *) &stime)));
+#endif
+             res = LoadPacket(stime, reggid, &retrcart);
+           }
+           if (res == ERR_NONE) {
+             /* NetPacketize it */
+             MakeDataPkt(&outpktbuf, &retrcart);
+             rhost.sin_port = htons(BASKETPORT);
+             res = NetTalk(&rhost, outpktbuf, TRUE);
+             if (res < 0) {
+               fprintf(stderr, "NetTalk failed (%d)\n", res);
+             }
+             free(outpktbuf);
+             lastDataSent = lastsend = getzulutime(NULL);
+           } else if (res == ERR_EOF) {
+             /* End of data.  If DND, bring down connection */
+             if (eggtable[0].conntype == CONN_DND) {
+               NetDown(eggtable[0].dncmd, sdlisten);
+               sdlisten = -1;
+             }
+             havedata = 0;
+           }
+           break;
+           
+         case SETTINGS_PACKET:
+           /* Update settings in buffer, will change at next packet
+              boundary. */
+           
+           { char *pktP = pktbuf + (3 * sizeof(uint16)) + sizeof(uint32);
+           
+           unpackShort(protocol.samp_rec);
+           unpackShort(protocol.sec_rec);
+           unpackShort(protocol.rec_pkt);
+           unpackByte(protocol.trialsz);
+           }
+#ifdef PACKETDUMP
+           fprintf(stderr, "Settings: samp_rec = %d, sec_rec = %d, rec_pkt = %d, trialsz = %d\n",
+                   protocol.samp_rec, protocol.sec_rec, protocol.rec_pkt, protocol.trialsz);
+#endif
+           break;
+         }
+         free(pktbuf);
+       }
+      }
+      gettimeofday(&ct, NULL);
+    } while (havedata && ct.tv_usec < 1000000L - MINSLEEP);
+
+    /* 2H. Compute the length in microseconds we should sleep before
+       the top of the next second.  Note that we compute the interval
+       based on the time at the bottom of the loop, after dealing with
+       whatever housekeeping requests may have been performed since
+       collecting the last sample. */
+
+    gettimeofday(&ct, NULL);
+    if (lt.tv_sec == 0) {
+       lt.tv_sec = t.tv_sec - 1;
+    }
+    time_latency = (((t.tv_sec - lt.tv_sec) - 1) * 1000000L) + t.tv_usec;
+    time_housekeeping = ((ct.tv_sec - t.tv_sec) * 1000000L) + (ct.tv_usec - t.tv_usec);
+#ifdef DEBUG
+    usec = time_latency;
+    msec = usec / 1000;
+    usec %= 1000;
+    printf("Sampling latency: %ld.%03ld msec", msec, usec);
+
+    usec = time_housekeeping;
+    msec = usec / 1000;
+    usec %= 1000;
+    printf("  Housekeeping time: %ld.%03ld msec\n", msec, usec);
+#endif
+    lt = ct;
+    sleeptime = ((1000000 - SLACK) - ct.tv_usec);
+    /* printf("Sleep time = %d usec\n", sleeptime); */
+  }
+}
+
+/* EGG specific */
+
+/*  LoadRCFile --  Read in a configuration file.  If the filename
+                   argument is NULL, the default file is read.
+                   Otherwise, configuration statements are read
+                   from the file given by the argument.  */
+
+static void LoadRCFile(char *filename) {
+  FILE *fp;
+  char linebuf[200];
+  char *myargv[MAX_PARSE], *tp;
+  int myargc, lcount, p, b, i;
+
+  if (filename != NULL) {
+    if ((fp = fopen(filename, "r")) == NULL) {
+        fprintf(stderr, "%s: Cannot open auxiliary configuration file %s.\n", pgmname, filename);
+       exit(-1);
+    }
+  } else {
+    if ((fp = fopen(".eggrc", "r")) == NULL) {
+      if ((fp = fopen("~/.eggrc", "r")) == NULL) {
+        if ((fp = fopen("/etc/eggrc", "r")) == NULL) {
+          fprintf(stderr, "%s: Couldn't find a egg RC file.\n", pgmname);
+         exit(-1);
+       }
+      }
+    }
+  }
+
+  lcount = 0;
+
+  while(fgets(linebuf, 200, fp) != NULL) {
+    /* Comments and blank lines ignored. */
+    lcount++;
+    if (*linebuf == '#' || *linebuf == '\n') continue;
+    Parse(linebuf, &myargc, myargv);
+    if (myargc == 0) continue;
+    if (!strcmp(myargv[0], "EGG")) {
+      if (myargc < 7 || myargc > 8) {
+        fprintf(stderr, "%s: RC error, line %d, poor %s format\n", 
+               pgmname, lcount, myargv[0]);
+       exit(-1);
+      }
+      if (numeggs > 0) {
+        fprintf(stderr, "%s: RC error, line %d, only one EGG allowed\n", 
+               pgmname, lcount);
+       exit(-1);
+      }
+
+      eggtable[0].name = mallocpy(myargv[1]);
+      eggtable[0].id = atoi(myargv[2]);
+      dquad2sockaddr(&(eggtable[0].ipaddr), NULL, myargv[3]);
+      eggtable[0].primbasket = mallocpy(myargv[4]);
+      eggtable[0].conntype = (!strcmp(myargv[5], "PERM"))?CONN_PERM:CONN_DND;
+      eggtable[0].connival = atoi(myargv[6]);
+      eggtable[0].url = NULL;  /* URL specification is permitted, but ignored */
+      numeggs = 1;
+    } else if (!strcmp(myargv[0], "BASKET")) {
+      if (myargc != 3) {
+        fprintf(stderr, "%s: RC error, line %d, poor %s format\n", 
+               pgmname, lcount, myargv[0]);
+       exit(-1);
+      }
+      baskettable[numbaskets].name = mallocpy(myargv[1]);
+      dquad2sockaddr(&(baskettable[numbaskets].ipaddr), NULL, myargv[2]);
+      numbaskets++;
+    } else if (!strcmp(myargv[0], "PROTOCOL")) {
+      if (myargc != 5) {
+        fprintf(stderr, "%s: RC error, line %d, poor %s format\n", 
+               pgmname, lcount, myargv[0]);
+       exit(-1);
+      }
+      protocol.samp_rec = atoi(myargv[1]);
+      protocol.sec_rec = atoi(myargv[2]);
+      protocol.rec_pkt = atoi(myargv[3]);
+      protocol.trialsz = atoi(myargv[4]);
+
+    /* REG:  Configure Random Event Generator for this egg.  */
+
+    } else if (!strcmp(myargv[0], "REG")) {
+      if (myargc != 4) {
+        fprintf(stderr, "%s: RC error, line %d, poor %s format\n", 
+               pgmname, lcount, myargv[0]);
+       exit(-1);
+      }
+      if (configuredREG != NULL) {
+        fprintf(stderr, "%s: RC error, line %d, multiple REGs specified.\n",
+               pgmname, lcount);
+       exit(-1);
+      }
+      for (i = 0; reg_table[i] != NULL; i++) {
+       if (strcasecmp(myargv[1], reg_table[i]->reg_name) == 0) {
+         configuredREG = reg_table[i];
+         devopts.type = reg_table[i]->reg_type;
+       }
+      }
+      if (configuredREG == NULL) {
+        fprintf(stderr, "%s: RC error, line %d, unknown REG type %s specified.\n",
+               pgmname, lcount, myargv[1]);
+       exit(-1);
+      }
+       
+      devopts.port = atoi(myargv[2]);
+      devopts.baud = atoi(myargv[3]);
+    } else if (!strcmp(myargv[0], "NETUP") ||
+               !strcmp(myargv[0], "NETDOWN") ||
+              !strcmp(myargv[0], "PORT") ||
+              !strcmp(myargv[0], "INTERFACE")) {
+      if (myargc < 2) {
+        fprintf(stderr, "%s: RC error, line %d, poor %s format\n", 
+               pgmname, lcount, myargv[0]);
+       exit(-1);
+      }
+      tp = (char *) malloc(200);
+      for (*tp = 0, p = 1; p < myargc; p++) {
+       strcat(tp, myargv[p]);
+        if (p < myargc-1) strcat(tp, " ");
+      }
+
+      if (!strcmp(myargv[0], "NETUP")) eggtable[0].upcmd = tp;
+      else if (!strcmp(myargv[0], "NETDOWN")) eggtable[0].dncmd = tp;
+      else if (!strcmp(myargv[0], "PORT")) {
+       if (atoi(tp) <= 0) {
+         fprintf(stderr, "%s: RC error, ignoring bad port %d, using %d\n",
+                 pgmname, atoi(tp), myport);
+       } else {
+         myport = atoi(tp);
+       }
+       free(tp);
+      } else if (!strcmp(myargv[0], "INTERFACE")) myaddr = tp;
+    } else {
+      fprintf(stderr, "%s: RC error, %s is unknown keyword\n", pgmname, myargv[0]);
+      exit(-1);
+    }
+  }
+
+  /* Consistency checks done only after loading the main
+     configuration file. */
+
+  if (filename == NULL) {
+    for (p = -1, b = 0; b < numbaskets; b++) {
+      if (!strcmp(baskettable[b].name, eggtable[0].primbasket)) p = b;
+    }
+
+    if (p != 0) {
+      fprintf(stderr, "%s: RC error, primary basket should be listed first.\n", pgmname);
+      exit(-1);
+    }
+  } else {
+      printf("Reloaded auxiliary configuration file %s.\n", filename);
+  }
+  fclose(fp);
+}
+
+/*  MakeAwake  --  Make a egg awake packet to let the basket
+                   know we're on line.  */
+
+static void MakeAwake(AwakePacket *pkt) {
+  char *pktP = (char *) pkt;
+
+  packShort(AWAKE_PACKET);
+  packShort((4 * sizeof(uint16)) + sizeof(uint32));
+  packShort(eggtable[0].id);
+  packLong(getzulutime(NULL));
+}
+
+/*  MakeDataPkt  --  Build a canonical network byte order data packet
+                    from the data collected in an EggCarton.  */
+
+static void MakeDataPkt(char **pkt, EggCarton *src) {
+  uint16 pktsize, rec;
+  char *pktP;
+
+  pktsize = (7 * sizeof(uint16)) + sizeof(trial) +         /* Header */
+           (src->hdr.numrec * (sizeof(uint32) +            /* Trial data */
+               src->hdr.samp_rec * sizeof(trial))) +
+           sizeof(uint16);                                 /* CRC-16 */
+  *pkt = pktP = (char *) malloc(pktsize);
+
+  /* Assemble header fields into data packet. */
+
+  packShort(src->hdr.type = DATA_PACKET);
+  packShort(src->hdr.pktsize = pktsize);
+  packShort(src->hdr.eggid);
+  packShort(src->hdr.samp_rec);
+  packShort(src->hdr.sec_rec);
+  packShort(src->hdr.rec_pkt);
+  packByte(src->hdr.trialsz);
+  packShort(src->hdr.numrec);
+
+  /* Append data records to packet. */
+
+  for (rec = 0; rec < src->hdr.numrec; rec++) {
+    packLong(src->records[rec].timestamp);
+    packBytes(&(src->records[rec].trials), src->hdr.samp_rec);
+  }
+
+  /* No need to calculate CRC -- NetTalk does that for us.
+     Note that we did reserve space for the CRC when
+     allocating the packet buffer. */
+}
+
+/*  SetCollOpts  --  Set collection options from the protocol
+                    specified in the .rc file or by a basket.  */
+
+static double SetCollOpts(void) {
+  double sps;
+
+  coll.opts.eggid = eggtable[0].id;
+  
+  coll.opts.rec_pkt = protocol.rec_pkt;
+  if (coll.opts.rec_pkt < 1) coll.opts.rec_pkt = 1;
+  if (coll.opts.rec_pkt > MAXREC_PKT) coll.opts.rec_pkt = MAXREC_PKT;
+
+  coll.opts.trialsz = protocol.trialsz;
+  if (coll.opts.trialsz < MINBITS) {
+    fprintf(stderr, "Attempt to set trial size below %d, to %d.\n",
+           MINBITS, coll.opts.trialsz);
+    coll.opts.trialsz = MINBITS;
+  }
+  if (coll.opts.trialsz > MAXBITS) {
+    fprintf(stderr, "Attempt to set trial size above %d, to %d.\n",
+           MAXBITS, coll.opts.trialsz);
+    coll.opts.trialsz = MAXBITS;
+  }
+
+  coll.opts.sec_rec = protocol.sec_rec;
+  if (coll.opts.sec_rec < 1) {
+    fprintf(stderr, "Attempt to set seconds/record below 1, to %d.\n",
+           coll.opts.sec_rec);
+    coll.opts.sec_rec = 1;
+  }
+  if (coll.opts.sec_rec > MAXSEC_REC) {
+    fprintf(stderr, "Attempt to set seconds/record above %d, to %d.\n",
+           MAXSEC_REC, coll.opts.sec_rec);
+    coll.opts.sec_rec = MAXSEC_REC;
+  }
+
+  coll.opts.samp_rec = protocol.samp_rec;
+  if (coll.opts.samp_rec < 1) {
+    fprintf(stderr, "Attempt to set samples/record below 1, to %d.\n",
+           coll.opts.samp_rec);
+    coll.opts.samp_rec = 1;
+  }
+  if (coll.opts.samp_rec > MAXSAMP_REC) {
+    fprintf(stderr, "Attempt to set samples/record above %d, to %d.\n",
+           MAXSAMP_REC, coll.opts.samp_rec);
+    coll.opts.samp_rec = MAXSAMP_REC;
+  }
+
+  sps = (double)coll.opts.samp_rec / (double)coll.opts.sec_rec;
+
+  fprintf(stderr, "Effective sample rate is about %f samp/sec or %f bits/sec\n",
+         sps, coll.opts.trialsz * sps);
+  fprintf(stderr, "Packets contain %d records\n", coll.opts.rec_pkt);
+
+  return sps;
+}
+
+/*  GreetBasket  --  Send an awake packet to each configured
+                    basket.  */
+
+static int GreetBasket(void) {
+  int                  i, b;
+  struct sockaddr_in   bhost;
+  AwakePacket          awake;
+
+  MakeAwake(&awake);
+
+  for (b = 0; b < numbaskets; b++) {
+    memset(&bhost, 0, sizeof(struct sockaddr_in));
+    memcpy(&bhost, &(baskettable[b].ipaddr), sizeof(bhost));
+    bhost.sin_port = htons(BASKETPORT);
+    bhost.sin_family = AF_INET;
+
+    /* Don't gripe about network unreachable.  Just means 
+       network is down right now, try later. */
+    i = NetTalk(&bhost, (char *)&awake, FALSE);
+    if (i < 0) {
+      /* Couldn't get to this one, try others. */
+#ifdef DEBUG
+      fprintf(stderr, "%s: Failure to reach basket '%s'\n", 
+             pgmname, baskettable[b].name);
+#endif
+    } else {
+      return ERR_NONE;
+    }
+  }
+
+  return ERR_COMM_TMOUT;
+}
+
+/*  handle_sigkill  -- KILL signal handler.  Terminate user interface
+                       and exit.  */
+
+static void handle_sigkill(int arg) {
+  UIClose();
+  exit(-1);
+}
+
+/*  handle_sighup  --  HUP signal handler.  Reload protocol from the
+                      .eggprotocolrc file.  */
+
+static void handle_sighup(int arg) {
+  LoadRCFile(".eggprotocolrc");
+#ifndef SIGACTION_WORKING
+  signal(SIGHUP, handle_sighup);
+#endif
+}
diff --git a/eggrc.sample b/eggrc.sample
new file mode 100644 (file)
index 0000000..b8c44bc
--- /dev/null
@@ -0,0 +1,86 @@
+# Configuration file for egg collection software
+# This specifies the egg configuration information, the contact
+# information for its basket(s), and initial data acquisition parameters.
+
+# Each line consists of a case-sensitive keyword and a series of
+# options.  Defined keywords are: 
+#
+#   EGG <name> <id> <ip addr> <primbasket> <conntype> <connival> <url>
+#
+# The primary basket <primbasket> is the name of the basket (defined
+# below) that should be contacted retrieve data from this Egg.
+# The connection type <conntype> is "PERM" (permanent) or "DND"
+# (dial-and-drop).  So far the code does not support the DND option.
+# The connection interval <connival> determines the time (in minutes)
+# between transmission attempts.  If specified, and not equal to
+# ".", <url> gives a URL (for example http://www.somesite.net/page.html)
+# which will be linked to the egg <name> in status reports.  Note that
+# this specification has meaning only in the .basketrc file.  For
+# compatibility, it is permitted in an .eggrc file, but is ignored.
+#
+#   BASKET <name> <ip addr>
+#
+# This entry tells us about the existence of a set of baskets.  The
+# basket named as primary is the one which will be contacted
+# initially. 
+#
+#   PROTOCOL <samprec> <secrec> <recpkt> <trialsz>
+#
+# Specify the default data collection protocol.  The arguments are 
+# <samprec> samples per record (1-10, though this should always be 5-10),
+# <secrec> seconds per records (1-3000),
+# <recpkt> records per packet (1-60), and 
+# <trialsz> bits per sample (32-255).
+#
+#   REG <type> <port> <baud>
+#
+# Specify hardware device.  Supported types include "PEAR" only at
+# this time.  Port is serial port number (e.g. 1 for /dev/ttyS1);
+# <baud> is baud rate.
+#
+#   NETUP <script> <args> ...
+#   NETDOWN <script> <args> ...
+#
+# Provide script files to be run to bring up and tear down the network
+# connection on demand.  These will only be used if connection type is
+# DND.
+
+#EGG halley 1 10.0.0.111 halley PERM 1
+#BASKET halley 10.0.0.111
+#EGG halley 1 10.0.0.111 mercury DND 3
+#BASKET mercury 10.0.0.125
+
+#EGG noosphere 28 128.112.35.133 noosphere PERM 1
+#BASKET noosphere 128.112.35.133
+
+#   diesse -> diesse
+#EGG diesse 37 193.8.230.134 diesse PERM 1
+#BASKET diesse 193.8.230.134
+
+#   noosphere -> diesse
+EGG noosphere 28 128.112.35.133 diesse PERM 1
+BASKET diesse 193.8.230.134
+
+#   throop -> diesse
+#EGG throop 1003 193.8.230.132 diesse PERM 1 http://www.fourmilab.ch/
+#BASKET diesse 193.8.230.134
+
+#   jura -> jura
+#EGG jura 1004 193.8.230.130 jura PERM 1 http://www.fourmilab.ch/
+#BASKET jura 193.8.230.130
+
+#   diesse -> noosphere
+#EGG diesse.fourmilab.ch 37 193.8.230.134 noosphere PERM 1
+#BASKET noosphere 128.112.35.133
+
+#EGG diesse 37 193.8.230.134 jura PERM 1
+#BASKET jura 193.8.230.130
+
+#BASKET tonga 209.157.90.137
+#BASKET tonga1 209.157.90.138
+PROTOCOL 10 10 6 200
+#REG PEAR 2 9600
+REG PSEUDO 1 9600
+#REG ORION 1 9600
+NETUP pppscript up
+NETDOWN pppscript down
diff --git a/eggui.c b/eggui.c
new file mode 100644 (file)
index 0000000..6404456
--- /dev/null
+++ b/eggui.c
@@ -0,0 +1,191 @@
+/* PROGRAM:    eggsh
+ * FILE:       $Header: /home/egg/src/RCS/eggui.c,v 1.8 1999/02/28 20:02:54 ghn Exp $
+ * PURPOSE:    EGG site user interface
+ * AUTHOR:     Greg Nelson
+ * DATE:       98-06-28
+ *
+ * REVISED:
+ * $Log: eggui.c,v $
+ * Revision 1.8  1999/02/28 20:02:54  ghn
+ * Version 5.1: The user interface now leaves the cursor at the bottom of
+ * the screen and responds to ^L to redraw the entire screen.
+ *
+ * Revision 1.7  1999/01/01 23:58:56  ghn
+ * Take out "CPU_BOUND" conditionals; we won't run CPU_BOUND any more.
+ *
+ * Revision 1.6  1998/12/31 22:07:56  ghn
+ * Rev 5 code: includes multi-reg support, HTML, etc.
+ *
+ * Revision 1.5  1998/08/03 20:36:28  kelvin
+ * Show time of last data collection by basket.
+ *
+ * Revision 1.4  1998/08/01  21:33:28  ghn
+ * Added real implementation for ncursed() based UI for egg.
+ *
+ * Revision 1.3  1998/08/01 18:51:25  ghn
+ * Added John's byte-order-independence changes.
+ *
+ * Revision 1.2  1998/08/01 17:05:48  ghn
+ * Trivial additions of curses related code.
+ *
+ * Revision 1.1  1998/07/21 11:41:23  ghn
+ * Initial revision
+ *
+ * Copyright 1998 - Greg Nelson
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <curses.h>
+
+/*  Curses takes it upon itself to define TRUE and FALSE,
+    incompatibly, as it happens, with the definitions in
+    global.h.  So, clear out the CURSES definitions.  */
+#ifdef TRUE
+#undef TRUE
+#endif
+#ifdef FALSE
+#undef FALSE
+#endif
+
+#include "global.h"
+#include "genlib.h"
+#include "errnos.h"
+#include "regs.h"
+#include "version.h"
+
+#ifndef NO_UI
+static int32 inittm = 0;
+#endif
+
+extern int32 lastDataSent;
+extern int32 time_latency, time_housekeeping;
+
+/* Initialize user interface, as needed. */
+int32 UIInit(void) {
+#ifndef NO_UI
+  initscr(); 
+  cbreak(); noecho(); 
+  nonl();
+  intrflush(stdscr, FALSE);
+  keypad(stdscr, TRUE);
+  timeout(0);
+  clear();
+  move(0, 0);
+  printw("Please wait, initializing...");
+  refresh();
+  inittm = 0;
+#endif
+  return ERR_NONE;
+}
+
+/* Close down user interface */
+int32 UIClose(void) {
+#ifndef NO_UI
+  endwin();
+#endif
+  return ERR_NONE;
+}
+
+/* Update user interface once on every data collection, receiving the
+   current collection result and CollectRecord. */
+int32 UIUpdate(int32 cres, CollectRecord *coll) {
+#ifndef NO_UI
+  char *tmstr;
+  double recavg;
+  static double grandavg = 0;
+  static int32 totsamp = 0;
+  int32 i, line, now;
+
+  if ((i = getch()) != ERR) {
+    if (i == 12) clear();
+  }
+
+  if (inittm == 0) {
+    inittm = getzulutime(NULL);
+    clear();
+  }
+
+  /* Show protocol. */
+
+  if (coll->sampct == 1) {
+    line = 11;
+    move(line++, 5); printw("Samples per record: %3d", (int)coll->opts.samp_rec);
+    move(line++, 5); printw("Seconds per record: %3d", (int)coll->opts.sec_rec);
+    move(line++, 5); printw("Records per packet: %3d", (int)coll->opts.rec_pkt);
+    move(line++, 5); printw("Bits per trial:     %3d", (int)coll->opts.trialsz);
+    refresh();
+  }
+
+  if (cres < 0) return ERR_NONE;
+
+  line = 0;
+  move(line++, 3);
+  printw("EGG %s ID %d REG %s %s", eggtable[0].name, eggtable[0].id,
+        configuredREG->reg_name, Version);
+
+  tmstr = ctime(&inittm); tmstr[strlen(tmstr)-1] = 0;
+  move(line++, 5);
+  printw("Up since       %25s", tmstr);
+
+  move(line++, 5);
+  now = getzulutime(NULL);
+  tmstr = ctime(&now); tmstr[strlen(tmstr)-1] = 0;
+  printw("Last sample at %25s", tmstr);
+
+  move(line++, 5);
+  tmstr = ctime(&lastDataSent);
+  tmstr[strlen(tmstr)-1] = 0;
+  printw("Last packet at %25s", lastDataSent == 0 ? "Never" : tmstr);
+
+  /* Note that for a non-CPU_BOUND built, UIUpdate is passed a
+     collection record filled with the last 10 samples collected.
+     Since missing samples do not figure in this record, there
+     is no need to test EGG_MISSING_DATA when computing the
+     mean below. */
+
+  /* Show mean since egg started and, each time 10 samples
+     are collected, the mean for the last 10 samples. */
+
+  line++;
+  grandavg *= totsamp;
+  grandavg += coll->data.trials[coll->sampct-1]; totsamp++;
+  grandavg /= totsamp;
+  move(line++, 5);
+  printw("Grand mean:       %6.2f", grandavg);
+
+  if (cres == 1) {
+    for (recavg = 0, i = 0; i < coll->opts.samp_rec; i++)
+      recavg += coll->data.trials[i];
+    recavg /= coll->opts.samp_rec;
+    move(line, 5); 
+    printw("Last record mean: %6.2f (%d seconds)", recavg, coll->opts.sec_rec);
+  }
+  line++;                            /* Advance whether we show record mean or not */
+
+  /* Show latency (lapse between start of second as measured by
+     the computer's clock and when collection of the sample
+     actually began) and time elapsed in housekeeping (saving
+     packets in local files, talking to the basket, updating
+     the user interface, etc.) following collection of the sample.
+     If housekeeping time frequently consumes a substantial portion
+     of the inter-sample interval, the probability of lost
+     samples increases.  Both times are shown in milliseconds. */
+
+  line++;
+  move(line++, 5);
+  printw("Sampling latency:   ");
+  clrtoeol();
+  printw("%.3f ms", time_latency / 1000.0);
+  move(line++, 5);
+  printw("Housekeeping time:  ");
+  clrtoeol();
+  printw("%.3f ms", time_housekeeping / 1000.0);
+
+  move(16, 0);
+
+  refresh();
+#endif
+
+  return ERR_NONE;
+}
diff --git a/eggui.h b/eggui.h
new file mode 100644 (file)
index 0000000..6aa4090
--- /dev/null
+++ b/eggui.h
@@ -0,0 +1,29 @@
+/* PROGRAM:     eggsh
+ * FILE:        $Header: /home/egg/src/RCS/eggui.h,v 1.3 1999/01/02 00:01:40 ghn Exp $
+ * PURPOSE:     EGG site user interface
+ * AUTHOR:      Greg Nelson
+ * DATE:        98-06-28
+ *
+ * REVISED:
+ * $Log: eggui.h,v $
+ * Revision 1.3  1999/01/02 00:01:40  ghn
+ * Removed obsolete "collect.h" include.
+ *
+ * Revision 1.2  1998/12/31 22:11:05  ghn
+ *  Rev 5 code: includes multi-reg support, HTML, etc.
+ *
+ * Revision 1.1  1998/07/21 11:40:13  ghn
+ * Initial revision
+ * 
+ * Copyright 1998 - Greg Nelson
+ */
+
+/* Initialize user interface, as needed. */
+extern int32 UIInit(void);
+
+/* Close down user interface */
+extern int32 UIClose(void);
+
+/* Update user interface once on every data collection, receiving the
+   current collection result and CollectRecord. */
+extern int32 UIUpdate(int32 cres, CollectRecord *coll);
diff --git a/errnos.h b/errnos.h
new file mode 100644 (file)
index 0000000..28b0b10
--- /dev/null
+++ b/errnos.h
@@ -0,0 +1,62 @@
+/* PROGRAM:     all
+ * FILE:        $Header: /home/ghn/projects/PEAR/egg/eggsh/RCS/errnos.h,v 1.1 1998/07/21 11:39:53 ghn Exp $
+ * PURPOSE:     Definitions for error constants
+ * AUTHOR:      Greg Nelson
+ * DATE:        98-05-09
+ *
+ * REVISED:     $Log: errnos.h,v $
+ * REVISED:     Revision 1.1  1998/07/21 11:39:53  ghn
+ * REVISED:     Initial revision
+ * REVISED:
+ * Copyright 1998 - Greg Nelson
+ * Redistributable under the terms of the GNU Public Licence (GPL)
+ */
+
+/* 0 - no error */
+#define ERR_NONE        0
+
+/* -1 to -99 - generic errors */
+#define ERR_OTHER       -1      /* Catchall for other errors */
+#define ERR_NOMEM       -2      /* Could not allocate memory            */
+#define ERR_NIMP        -3      /* Not implemented */
+#define ERR_WRONGARGN   -4      /* Wrong number of arguments.           */
+#define ERR_INRANGE     -5      /* Input out of range */
+#define ERR_OTIMEOUT    -6      /* Got a timeout, not from COMM         */
+#define ERR_STRTOOLONG  -7      /* String was longer than allowed       */
+
+/* -100 to -199 - comm errors */
+#define ERR_STRAYINPUT  -100    /* Stray characters were received       */
+#define ERR_PKT_BADSTX  -101    /* Packet did not begin with STX        */
+#define ERR_PKT_BADLEN  -102    /* Length word could not be read        */
+#define ERR_PKT_SHORT   -103    /* Packet was not long enough           */
+#define ERR_PKT_CKSUM   -104    /* Packet checksum did not match        */
+#define ERR_PKT_BADETX  -105    /* Packet did not end with ETX          */
+#define ERR_COMM_TMOUT  -106    /* Timeout during communication         */
+#define ERR_COMM_BRK    -107    /* Received break character             */
+#define ERR_COMM_PARITY -108    /* Received parity error                */
+#define ERR_COMM_FRAME  -109    /* Received framing error               */
+#define ERR_COMM_OVERRN -110    /* Received overrun error               */
+#define ERR_BUFF_FULL   -111    /* Buffer was too full to queue msg     */
+#define ERR_NOREPLY     -112    /* Device did not reply to hails        */
+#define ERR_RESPINVAL   -113    /* Response was invalid                 */
+#define ERR_COMM_BTMOUT -114    /* Timeout inside body of message       */
+#define ERR_PORT_INACT  -115    /* Attempt to use an inactive port      */
+
+/* -200 to -299 - math errors */
+#define ERR_DIV0        -200    /* Attempt to divide by zero            */
+#define ERR_NOSOLN      -201    /* Couldn't solve for discontinuities   */
+#define ERR_APPROX      -202    /* Result was an inexact approximation  */
+#define ERR_NOVARS      -203    /* No parameters were variable          */
+#define ERR_SINGULAR    -204    /* Matrix was singular, unsolvable      */
+#define ERR_OVERFLOW    -205    /* Numerical overflow                   */
+
+/* -300 to -399 - DLL specific errors */
+#define ERR_NOTSUPP     -300    /* Current device does not support fn   */
+#define ERR_RESOURCE    -301    /* Ran out of a DLL limited resource    */
+#define ERR_DEVDRVR     -302    /* Windows device driver returned err   */
+#define ERR_DEVSELECT   -303    /* Selecting a device failed (general)  */
+#define ERR_NOENT       -320    /* File not found                       */
+#define ERR_CNREAD      -321    /* Couldn't read desired data from file */
+#define ERR_CNWRITE     -322    /* Couldn't write desired data to file  */
+#define ERR_EOF         -323    /* End of file reached unexpectedly     */
+#define ERR_DLGCAN      -324    /* Dialog canceled, not an "error"      */
diff --git a/examine.c b/examine.c
new file mode 100644 (file)
index 0000000..ac4d0e8
--- /dev/null
+++ b/examine.c
@@ -0,0 +1,61 @@
+/* PROGRAM:     examine
+ * FILE:        $Header: /home/egg/src/RCS/examine.c,v 1.1 1998/07/21 11:39:04 ghn Exp $
+ * PURPOSE:     Testing data retrieval from database
+ * AUTHOR:      Greg Nelson
+ * DATE:        98-05-09
+ *
+ * REVISED:     $Log: examine.c,v $
+ * REVISED:     Revision 1.1  1998/07/21 11:39:04  ghn
+ * REVISED:     Initial revision
+ * REVISED:
+ * Copyright 1998 - Greg Nelson
+ * Redistributable under the terms of the GNU Public Licence (GPL)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "global.h"
+#include "storage.h"
+
+EggEntry eggtable[MAX_EGGS];
+
+int main(int argc, char *argv[]) {
+  int i, j, k, trials;
+  FILE *fp;
+  EggCarton pktbuf;
+
+  pgmname = argv[0];
+
+  if (argc != 1 && argc != 2) {
+    printf("Usage: %s [infile]\n", pgmname);
+    exit(-1);
+  }
+  
+  InitStorage(NULL);
+
+  if (argc == 1) fp = stdin;
+  else fp = fopen(argv[1], "r");
+
+  trials = 0;
+  while (1) {
+    i = LoadPacket(fp, &pktbuf);
+    if (i < 0) {
+      printf("Total trials: %d\n", trials);
+      exit(-1);
+    }
+
+    printf("Packet of %d records read\n", pktbuf.hdr.numrec);
+    for (j = 0; j < pktbuf.hdr.numrec; j++) {
+      printf("Record: %d points\n  %ld: ",
+            pktbuf.hdr.samp_rec,
+            pktbuf.records[j].timestamp);
+      for (k = 0; k < pktbuf.hdr.samp_rec; k++) {
+       printf("%3d ", pktbuf.records[j].trials[k]);
+       trials++;
+      }
+      printf("\n");
+    }
+  }
+
+  return 0;
+}
diff --git a/genlib.c b/genlib.c
new file mode 100644 (file)
index 0000000..de328ff
--- /dev/null
+++ b/genlib.c
@@ -0,0 +1,254 @@
+/* PROGRAM:     eggsh
+ * FILE:        $Header: /home/egg/src/RCS/genlib.c,v 1.6 1999/02/28 20:05:18 ghn Exp $
+ * PURPOSE:     General library
+ * AUTHOR:      Greg Nelson
+ * DATE:        98-05-09
+ *
+ * REVISED:
+ * $Log: genlib.c,v $
+ * Revision 1.6  1999/02/28 20:05:18  ghn
+ * Version 5.1: Changed dquad2sockaddr interface to support ip/mm mask,
+ * created hl2dquad to translate host-long to dotted quad, and modified
+ * sockaddr2dquad to use this.
+ *
+ * Revision 1.5  1998/12/31 22:07:56  ghn
+ * Rev 5 code: includes multi-reg support, HTML, etc.
+ *
+ * Revision 1.4  1998/08/03 20:43:35  kelvin
+ * File byte-order independence.
+ *
+ * Revision 1.3  1998/08/01  18:51:25  ghn
+ * Added John's byte-order-independence changes.
+ *
+ * Revision 1.2  1998/08/01 17:07:29  ghn
+ * Casting fixes from John plus better parsing.
+ *
+ * Revision 1.1  1998/07/21 11:38:15  ghn
+ * Initial revision
+ * 
+ * Copyright 1998 - Greg Nelson
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/utsname.h>
+#include "global.h"
+#include "genlib.h"
+
+int32 getzulutime(struct timeval *ztv) {
+  struct timeval ttv;
+
+  if (ztv) {
+    gettimeofday(ztv, NULL);
+    return ztv->tv_sec;
+  } else {
+    gettimeofday(&ttv, NULL);
+    return ttv.tv_sec;
+  }
+}
+
+/* Get time difference (tv1-tv2) in msec.  Only works if less than 24
+   days have passed. */
+int32 deltams(struct timeval *tv1, struct timeval *tv2) {
+  return (tv1->tv_sec - tv2->tv_sec) * 1000L +
+         (tv1->tv_usec - tv2->tv_usec) / 1000L;
+}
+
+/* Look up an EGG id.  If we can't get it from environment, do a nutty
+   thing using CRC of uname block, which should be pretty unique. */
+uint16 GetID(void) {
+  char *idstr;
+  struct utsname utsname;
+
+  if ((idstr = getenv("EGGID")) != NULL) {
+    return atoi(idstr);
+  } else {
+    uname(&utsname);
+    return BlockCRC16((byte *)(&utsname), sizeof(struct utsname));
+  }
+}
+
+char *Packetize(EggCarton *src) {
+  char *rbuf;
+  uint32 lbuf;
+  uint16 pktsize, rec;
+  char *pktP;
+
+  pktsize = (7 * sizeof(uint16)) + sizeof(trial) +         /* Header */
+           sizeof(char) +                                  /* ...including pad byte for trialsz */
+           (src->hdr.numrec * (sizeof(uint32) +            /* Trial data */
+               src->hdr.samp_rec * sizeof(trial))) +
+           sizeof(uint32);                                 /* CRC and terminator */
+  rbuf = pktP = (char *) malloc(pktsize);
+
+  /* Assemble header fields into data packet. */
+
+  packShort(src->hdr.type = DATA_PACKET);
+  packShort(src->hdr.pktsize = pktsize);
+  packShort(src->hdr.eggid);
+  packShort(src->hdr.samp_rec);
+  packShort(src->hdr.sec_rec);
+  packShort(src->hdr.rec_pkt);
+  packByte(0);                       /* Pad byte in case we want to expand trialsz */
+  packByte(src->hdr.trialsz);
+  packShort(src->hdr.numrec);
+
+  /* Append data records to packet. */
+
+  for (rec = 0; rec < src->hdr.numrec; rec++) {
+    packLong(src->records[rec].timestamp);
+    packBytes(&(src->records[rec].trials), src->hdr.samp_rec);
+  }
+  /* Get CRC, pack into base(32,32,64) notation, and add tag byte (0xFF) */
+  lbuf = BlockCRC16((byte *) rbuf, pktP - rbuf);
+  lbuf = ((lbuf & 0xF800) << 13) |
+        ((lbuf & 0x07C0) << 10) |
+        ((lbuf & 0x003F) << 8) |
+        (0x00FF);
+  packLong(lbuf);
+  if ((pktP - rbuf) != pktsize) {
+    fprintf(stderr, "Length mismatch assembling packet.  Computed: %d, actually packed: %d.\n",
+       pktsize, pktP - rbuf);
+  }
+  return rbuf;
+}
+
+int32 Unpacketize(EggCarton *dst, char *src) {
+  char *pktP = src;
+  char pad;
+  uint16 rec;
+  uint32 lbuf, filecrc;
+
+  /* Unpack the portable header into a host-order and aligned
+     EggHeader packet. */
+
+  unpackShort(dst->hdr.type);
+  unpackShort(dst->hdr.pktsize);
+  unpackShort(dst->hdr.eggid);
+  unpackShort(dst->hdr.samp_rec);
+  unpackShort(dst->hdr.sec_rec);
+  unpackShort(dst->hdr.rec_pkt);
+  unpackByte(pad);                   /* Pad in case we later grow trialsz */
+  unpackByte(dst->hdr.trialsz);
+  unpackShort(dst->hdr.numrec);
+
+  if (dst->hdr.type != DATA_PACKET) {
+#ifdef DEBUG
+    fprintf(stderr, "Invalid header type 0x%04X in packet read from file.\n", dst->hdr.type);
+#endif
+    return -1;
+  }
+
+  /* Unpack the data records from the file packet. */
+
+  for (rec = 0; rec < dst->hdr.numrec; rec++) {
+    unpackLong(dst->records[rec].timestamp);
+    /* Assumes sizeof(trial) = 1 */
+    unpackBytes(&(dst->records[rec].trials), dst->hdr.samp_rec);
+  }
+
+  /* Compute the CRC, reassemble into record terminator,
+     and compare with terminator in file. */
+
+  lbuf = BlockCRC16((byte *) src, pktP - src);
+  lbuf = ((lbuf & 0xF800) << 13) |
+        ((lbuf & 0x07C0) << 10) |
+        ((lbuf & 0x003F) << 8) |
+        (0x00FF);
+
+  unpackLong(filecrc);
+
+  if (lbuf != filecrc) {
+#ifdef DEBUG
+    fprintf(stderr, "Bad CRC in packet read from file.  Read 0x%08lX, computed 0x%08lX.\n", filecrc, lbuf);
+#endif
+    return -2;
+  }
+
+  if (dst->hdr.pktsize != (pktP - src)) {
+#ifdef DEBUG
+    fprintf(stderr, "Length mismatch decoding packet.  Header: %d, length decoded: %d.\n",
+      dst->hdr.pktsize, pktP - src);
+#endif
+    return -1;
+  }
+
+  /* One final little tweak.  Since we included a pad byte to allow
+     for growth in trialsz, hdr.pktsize will include it.  Subtract
+     one to hide the existence of the pad in the file from the
+     caller.  In all probability the caller isn't going to look at
+     pktsize, but you can't be too careful. */
+
+  dst->hdr.pktsize--;
+
+  return 0;
+}
+
+void Parse(char *input, int *argc, char *argv[]) {
+  char *tp;
+
+  *argc = 0;
+
+  tp = strtok(input, " \t\n");
+  while (tp != NULL && *argc < MAX_PARSE) {
+    argv[*argc] = tp;
+    *argc += 1;
+    tp = strtok(NULL, " \t\n");
+  }
+}
+
+char *mallocpy(char *input) {
+  char *res;
+
+  res = (char *)malloc(1+strlen(input));
+  if (res) strcpy(res, input);
+  return res;
+}
+
+void dquad2sockaddr(struct sockaddr_in *sinp, int16 *mask, char *dquad) {
+  char *tp, *loser;
+  long saddr;
+  short qcount;
+
+  loser = mallocpy(dquad);
+
+  tp = strtok(loser, ".");
+  for (qcount = 0, saddr = 0; qcount < 4 && tp != NULL; qcount++) {
+    saddr = (saddr << 8) | (atoi(tp) & 0xFF);
+    tp = strtok(NULL, ".");
+  }
+  if (mask) {
+    *mask = 32;
+    strcpy(loser, dquad);
+    tp = strtok(loser, "/");
+    if (tp) {
+      tp = strtok(NULL, "/");
+      if (tp) *mask = atoi(tp);
+    }
+  }
+  free(loser);
+
+  sinp->sin_family = AF_INET;
+  sinp->sin_port = 0;          /* To be filled in later */
+  sinp->sin_addr.s_addr = htonl(saddr);
+}
+
+char *sockaddr2dquad(struct sockaddr_in *sinp) {
+  long saddr;
+  
+  saddr = ntohl(sinp->sin_addr.s_addr);
+  return hl2dquad(saddr);
+}
+
+char *hl2dquad(long addr) {
+  static char resout[16];
+
+  sprintf(resout, "%ld.%ld.%ld.%ld",
+         (addr >> 24) & 0xFF, (addr >> 16) & 0xFF,
+         (addr >> 8) & 0xFF, addr & 0xFF);
+  
+  return resout;
+}
diff --git a/genlib.h b/genlib.h
new file mode 100644 (file)
index 0000000..b176de6
--- /dev/null
+++ b/genlib.h
@@ -0,0 +1,75 @@
+/* PROGRAM:     eggsh
+ * FILE:        $Header: /home/egg/src/RCS/genlib.h,v 1.3 1999/02/28 20:05:35 ghn Exp $
+ * PURPOSE:     Define general library interface
+ * AUTHOR:      Greg Nelson
+ * DATE:        98-05-09
+ *
+ * REVISED:
+ * $Log: genlib.h,v $
+ * Revision 1.3  1999/02/28 20:05:35  ghn
+ * Version 5.1: Changed dquad2sockaddr interface to support ip/mm mask,
+ * created hl2dquad to translate host-long to dotted quad, and modified
+ * sockaddr2dquad to use this.
+ *
+ * Revision 1.2  1998/12/31 22:11:05  ghn
+ *  Rev 5 code: includes multi-reg support, HTML, etc.
+ *
+ * Revision 1.1  1998/07/21 11:38:04  ghn
+ * Initial revision
+ *
+ * Copyright 1998 - Greg Nelson
+ */
+
+#ifndef _GENLIB_H
+
+#include <unistd.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include "global.h"
+
+#define MAX_PARSE      20
+
+#ifdef OBSOLETE
+/* This is actually 366 * 24 * 60 * 60, as opposed the actual length
+   of the mean solar year which is about 365.2524 days.  */
+#define SEC_PER_YEAR    31622400L
+#endif
+
+/* Get time in GMT reference frame.  If ztv is NULL, simply returns
+   seconds.  Otherwise, returns complete timeval in ztv as well. */
+extern int32 getzulutime(struct timeval *ztv);
+
+/* Get difference in msec between two tvs. */
+extern int32 deltams(struct timeval *tv1, struct timeval *tv2);
+
+/* Look up an EGG id. */
+extern uint16 GetID(void);
+
+/* Create a proper checksummed and packetized byte stream from the raw
+   data in memory.  Return result is malloc'ed, so should be freed by
+   caller. */
+extern char *Packetize(EggCarton *src);
+
+extern int32 Unpacketize(EggCarton *dst, char *src);
+
+/* Compute CRC16 checksum given block and length (from crc16.c) */
+extern uint16 BlockCRC16(byte *block, uint32 len);
+
+extern void Parse(char *input, int *argc, char *argv[]);
+extern char *mallocpy(char *input);
+extern void dquad2sockaddr(struct sockaddr_in *sinp, int16 *mask, char *dquad);
+extern char *sockaddr2dquad(struct sockaddr_in *sinp); /* Static buffer! */
+extern char *hl2dquad(long addr);      /* Static buffer! */
+
+
+/* Access to system or emulated usleep(). */
+
+#ifdef USLEEP
+extern void sf_usleep(unsigned t);
+#define Usleep sf_usleep
+#else
+#define Usleep usleep
+#endif
+
+#define _GENLIB_H
+#endif /* _GENLIB_H */
diff --git a/global.h b/global.h
new file mode 100644 (file)
index 0000000..d85b156
--- /dev/null
+++ b/global.h
@@ -0,0 +1,186 @@
+/* PROGRAM:     eggsh
+ * FILE:        $Header: /home/egg/src/RCS/global.h,v 1.5 1999/02/28 20:10:29 ghn Exp $
+ * PURPOSE:     Declare global types, constants, and variables
+ * AUTHOR:      Greg Nelson
+ * DATE:        98-04-12
+ *
+ * REVISED:
+ * $Log: global.h,v $
+ * Revision 1.5  1999/02/28 20:10:29  ghn
+ * Version 5.1: Added netmask field for egg.
+ *
+ * Revision 1.4  1998/12/31 22:11:05  ghn
+ *  Rev 5 code: includes multi-reg support, HTML, etc.
+ *
+ * Revision 1.3  1998/08/01 18:51:25  ghn
+ * Added John's byte-order-independence changes.
+ *
+ * Revision 1.2  1998/08/01 17:08:20  ghn
+ * Added upcmd and dncmd for DND.
+ *
+ * Revision 1.1  1998/07/21 11:37:56  ghn
+ * Initial revision
+ *
+ * Copyright 1998 - Greg Nelson
+ * Redistributable under the terms of the GNU Public Licence (GPL)
+ */
+
+#ifndef _GLOBAL_H
+
+#include <netinet/in.h>
+
+#include "byteorder.h"         /* Build byte-order independent version */
+
+/* Good for i386, but be careful... */
+typedef unsigned char byte;
+typedef unsigned char uint8;
+typedef unsigned short uint16;
+typedef unsigned long uint32;
+typedef char int8;
+typedef short int16;
+typedef long int32;
+
+/* Some fixed assumptions:
+   Trial type is always bitsum.
+   Bit spacing method does not need to be communicated, since we'll
+     choose something at compile time. 
+   Record size ranges from one to MAXSAMP_REC trials, which are assumed to
+     be evenly spaced starting at timestamp.
+   Records always begin on round seconds divisible by sec_rec, which
+     can range from one to MAXSEC_REC.
+*/
+
+/* Some adjustable assumptions: */
+
+#define MINBITS                32      /* Minimum bits per trial */
+#define MAXBITS                253     /* Maximum bits per trial */
+#define MAXSAMP_REC    10      /* Maximum samples/record */
+#define MAXSEC_REC     3000    /* Maximum seconds/record */    
+
+#define SEC_PKT                60      /* Desired number of seconds per packet */
+#define MAXREC_PKT     60      /* Maximum number of records per packet
+                                  For 1-second records (typ) this is 1 min */
+
+#define EGG_MISSING_DATA 254    /* Value used to represent missing data in trials array. */
+
+#if MAXBITS < 256
+typedef uint8 trial;
+#else
+#error "Can't represent MAXBITS in a byte value."
+#endif
+
+#if MAXBITS >= EGG_MISSING_DATA
+#error "MAXBITS is >= EGG_MISSING_DATA: ambiguous trial data possible."
+#endif
+
+typedef struct eggrec {
+  uint32 timestamp;    /* Seconds since the epoch (19700101000000Z) */
+  trial trials[MAXSAMP_REC]; /* Trial values */
+} EggRec;              /* Normally 16 bytes total */
+
+/* Make sure type field of packet is non-zero so that CRC works. */
+
+typedef struct genpacket {
+  uint16 type;
+  uint16 pktsize;
+} GenPacket;
+
+#define DATA_PACKET    0x0101
+typedef struct eggheader {
+  uint16 type;         /* type */
+  uint16 pktsize;      /* Number of bytes in packet */
+  uint16 eggid;                /* Identifying number for device */
+  uint16 samp_rec;     /* Number of samples per record */
+  uint16 sec_rec;      /* Number of seconds per record */
+  uint16 rec_pkt;      /* Number of records per packet */
+  trial trialsz;       /* Number of bits per trial */
+  uint16 numrec;       /* Number of valid records */
+} EggHeader;
+
+#define REQ_PACKET     0x0202
+typedef struct reqpacket {
+  uint16 type;         /* type */
+  uint16 pktsize;      /* Number of bytes in packet */
+  uint16 eggid;                /* Identifying number for device */
+  uint32 starttm;      /* Time for first record to send */
+  uint16 cksum;                /* No data, checksum goes inside */
+} ReqPacket;
+
+#define AWAKE_PACKET   0x0303
+typedef struct awakepacket {
+  uint16 type;         /* type */
+  uint16 pktsize;      /* Number of bytes in packet */
+  uint16 eggid;                /* Identifying number for device */
+  uint32 nowtm;                /* Egg's identification of "now" (zulu) */
+  uint16 cksum;                /* No data, checksum goes inside */
+} AwakePacket;
+
+#define SETTINGS_PACKET        0x0404
+typedef struct setpacket {
+  uint16 type;         /* type */
+  uint16 pktsize;      /* Number of bytes in packet */
+  uint16 eggid;                /* Identifying number for device */
+  uint32 nowtm;                /* Egg's identification of "now" (zulu) */
+  uint16 samp_rec;     /* Number of samples per record */
+  uint16 sec_rec;      /* Number of seconds per record */
+  uint16 rec_pkt;      /* Number of records per packet */
+  trial trialsz;       /* Number of bits per trial */
+  uint16 cksum;                /* No data, checksum goes inside */
+} SettingsPacket;
+
+/*  An EggCarton is the in-memory representation of a data
+    packet.  Fields in it are thus in host byte order,
+    being translated, as appropriate, when transferred
+    to and from a DATA_PACKET by Packetize() and
+    Unpacketize().  */
+
+typedef struct eggpkt {
+  EggHeader hdr;       /* Header (type = DATA_PACKET) */
+  EggRec records[MAXREC_PKT];
+} EggCarton;
+
+typedef struct collect {
+  EggHeader opts;
+  EggRec data;
+  uint16 sampct;               /* Count of completed samples in rec */
+  int32 dd;                    /* Device handle */
+} CollectRecord;
+
+#define CONN_PERM      0
+#define CONN_DND       1
+
+typedef struct eggentry {
+  char *name;
+  int16 id;
+  struct sockaddr_in ipaddr;
+  int16 netmask;       /* Number of significant bits, default 32. */
+  char *primbasket;
+  int16 conntype;
+  int16 connival;      /* Connect interval in minutes */
+  int32 lastupd;       /* Zulutime of last update received */
+  int16 setup;         /* Does setup match config file? */
+  char *upcmd;         /* Command to bring net up (req if conntype == DND) */
+  char *dncmd;         /* Command to bring net down */
+  char *url;           /* URL describing basket or NULL */
+} EggEntry;
+
+typedef struct basketentry {
+  char *name;
+  struct sockaddr_in ipaddr;
+} BasketEntry;
+
+#define MAX_EGGS       100
+#define MAX_BASKETS    5
+
+extern EggEntry eggtable[MAX_EGGS];
+extern BasketEntry baskettable[MAX_BASKETS];
+extern short numeggs, numbaskets;
+extern EggHeader protocol;
+
+extern char *pgmname;  /* For error messages to announce program name */
+
+#define TRUE   -1
+#define FALSE  0
+
+#define _GLOBAL_H
+#endif /* _GLOBAL_H */
diff --git a/hw_pear.c b/hw_pear.c
new file mode 100644 (file)
index 0000000..401d307
--- /dev/null
+++ b/hw_pear.c
@@ -0,0 +1,241 @@
+/* PROGRAM:     eggsh
+ * FILE:        $Header: /home/egg/src/RCS/hw_pear.c,v 1.3 1998/08/01 18:50:39 ghn Exp $
+ * PURPOSE:     PEAR (Bradish box/micro-REG) hardware interface
+ * AUTHOR:      Greg Nelson
+ * DATE:        98-04-12
+ *
+ * REVISED:     $Log: hw_pear.c,v $
+ * REVISED:     Revision 1.3  1998/08/01 18:50:39  ghn
+ * REVISED:     Added John's byte-order-independence changes and PSEUDO support.
+ * REVISED:
+ * REVISED:     Revision 1.2  1998/08/01 17:13:51  ghn
+ * REVISED:     Added John's Solaris support and DUMPREG option.
+ * REVISED:
+ * REVISED:     Revision 1.1  1998/07/21 11:37:41  ghn
+ * REVISED:     Initial revision
+ * REVISED:
+ * Copyright 1998 - Greg Nelson
+ * Redistributable under the terms of the GNU Public Licence (GPL)
+ */
+
+/* Define this to dump samples from the REG which we actually use into
+   a file named dumpreg.dat.  This can be used to debug serial port
+   mode problems (for example, setting the character frame to 7 bits,
+   or telling it to ignore breaks, which will lose all zero bytes. */
+/* #define DUMPREG */
+
+#include <stdio.h>
+#define __USE_BSD
+#include <termios.h>
+#undef __USE_BSD
+#include <unistd.h>
+#include <fcntl.h>
+#define __USE_BSD
+#include <errno.h>
+#undef __USE_BSD
+
+#include "global.h"
+#include "genlib.h"
+#include "hwapi.h"
+
+#define MAXDEV 20
+
+int32 oldbits[MAXDEV], bitsleft[MAXDEV];
+
+#ifdef DUMPREG
+static FILE *dumpfile;               /* REG dump file handle */
+static unsigned char dumpbuf[1024];   /* REG dump buffer */
+static int dumpptr = 0;              /* Pointer into dump buffer */
+#endif
+
+#ifdef PSEUDORANDOM_TEST
+#include <stdlib.h>
+static int pseudo = FALSE;           /* Nonzero if using pseudo-REG */
+#endif
+
+int32 OpenDev(DevOpts *opts) {
+  char ttydev[30];
+  speed_t baudcon;
+  struct termios tt;
+  int32 TTY_fd, res;
+
+#ifdef DUMPREG
+  dumpfile = fopen("dumpreg.dat", "w");
+  setbuf(dumpfile, NULL);
+  dumpptr = 0;
+#endif
+
+#ifdef PSEUDORANDOM_TEST
+  if (opts->type == 0xFF) {
+    TTY_fd = 1;
+    oldbits[TTY_fd] = bitsleft[TTY_fd] = 0;
+    srandom(time(NULL));
+    pseudo = TRUE;
+    fprintf(stderr, "**** WARNING!  This egg has been configured to\n\
+               use a pseudorandom sequence generator to\n\
+               permit testing on machines not equipped\n\
+               with an REG.  Data from this egg should not\n\
+               be used for analysis.\n");
+    return TTY_fd;
+  }
+#endif
+
+#ifdef Solaris
+  /* Serial ports (at least built-in ones) have names of
+     /dev/term/a, /dev/term/b, etc.  Map the port numbers
+     from the RC file into this nomenclature, with "1"
+     designating /dev/term/a. */
+  sprintf(ttydev, "/dev/term/%c", 'a' + (opts->port - 1));
+#else
+  sprintf(ttydev, "/dev/ttyS%d", opts->port);
+#endif
+
+  switch(opts->baud) {
+  case 1200: baudcon = B1200; break;
+  case 2400: baudcon = B2400; break;
+  case 4800: baudcon = B4800; break;
+  case 9600: baudcon = B9600; break;
+  case 19200: baudcon = B19200; break;
+  case 38400: baudcon = B38400; break;
+#ifndef Irix
+  case 115200: baudcon = B115200; break;
+#endif
+  default:
+    printf("%s: Baud rate %ld not supported.\n", pgmname, opts->baud);
+    return -1;
+  }
+
+  fprintf(stderr, "Opening %s at %ld\n", ttydev, opts->baud);
+  if ((TTY_fd = open(ttydev, O_RDWR | O_NDELAY)) < 0) {
+    perror(pgmname);
+    return -1;
+  }
+
+  if (TTY_fd >= MAXDEV) {
+    fprintf(stderr, "%s: Too many devices open.\n", pgmname);
+    close(TTY_fd);
+    return -1;
+  }
+
+  res = tcgetattr(TTY_fd, &tt);
+#ifdef Solaris
+  tt.c_iflag = 0;
+  tt.c_oflag = 0;
+  tt.c_cflag = baudcon | CS8 | CREAD | CLOCAL;
+  tt.c_lflag = 0;
+  tt.c_cc[VMIN] = 1;   /* This many chars satisfies reads */
+  tt.c_cc[VTIME] = 0;  /* or in this many tenths of seconds */
+#else
+  res = cfsetospeed(&tt, baudcon); 
+  cfmakeraw(&tt);
+  tt.c_oflag &= (~(TABDLY | ONLCR));
+#endif
+  res = tcsetattr(TTY_fd, TCSANOW, &tt);
+
+  oldbits[TTY_fd] = bitsleft[TTY_fd] = 0;
+  return TTY_fd;
+}
+
+#define SAMP_PERIOD    1000    /* msec */
+#define MARGIN         .95     /* how much to headroom to allow in
+                                  speed measurement */
+
+int32 EvalSpeed(int32 dd) {
+  struct timeval start, end;
+  int32 bitct, samp;
+
+  if (dd < 0) return -1;
+
+#ifdef PSEUDORANDOM_TEST
+  if (pseudo) {
+    return 500;
+  }
+#endif
+
+  gettimeofday(&start, NULL);
+  bitct = 0;
+  while (1) {
+    gettimeofday(&end, NULL);
+    if (deltams(&end, &start) >= SAMP_PERIOD) break;
+    samp = Sample(dd, 1);
+    bitct++;
+  }
+
+  return (int32)(bitct * MARGIN);
+}
+
+int32 Sample(int32 dd, uint16 bits) {
+  int32 bc, sum, n1;
+  uint8 c1;
+
+  if (dd < 0) return -1;
+
+  sum = bc = 0;
+  while (bc < bits) {
+    if (bitsleft[dd]) {
+      sum += (oldbits[dd] & 0x01);
+      oldbits[dd] >>= 1;
+      bitsleft[dd]--;
+      bc++;
+    } else {
+      do {
+#ifdef PSEUDORANDOM_TEST
+       if (pseudo) {
+           n1 = 1;
+           c1 = (uint8) ((random() >> 5) & 0xFF);
+       } else
+#endif
+       n1 = read(dd, &c1, 1);
+      } while (n1 == 0 || (n1 == -1 && errno == EAGAIN));
+      if (n1 == -1) {
+       /* Fatal error occurred, die now? */
+       return -1;
+      }
+#ifdef DUMPREG
+      dumpbuf[dumpptr++] = c1;
+      if (dumpptr >= sizeof(dumpbuf)) {
+       fwrite(dumpbuf, sizeof(dumpbuf), 1, dumpfile);
+       dumpptr = 0;
+      }
+#endif
+      oldbits[dd] = c1;
+      bitsleft[dd] = 8;
+    }
+  }
+
+  return sum;
+}
+
+int32 Discard(int32 dd) {
+  int32 disc, n1;
+  uint8 c1;
+  
+  if (dd < 0) return -1;
+
+  disc = bitsleft[dd];
+  bitsleft[dd] = 0;
+
+#ifdef PSEUDORANDOM_TEST
+  if (pseudo) {
+    return 32;
+  }
+#endif
+
+  do {
+    n1 = read(dd, &c1, 1);
+    if (n1 == 1) disc++;
+  } while (n1 == 1);
+
+  return disc;
+}
+
+int32 CloseDev(int32 dd) {
+  if (dd < 0) return -1;
+
+#ifdef PSEUDORANDOM_TEST
+  if (!pseudo)
+#endif
+  close(dd);
+  return 0;
+}
+
diff --git a/hwapi.h b/hwapi.h
new file mode 100644 (file)
index 0000000..a9bfc62
--- /dev/null
+++ b/hwapi.h
@@ -0,0 +1,53 @@
+/* PROGRAM:     eggsh
+ * FILE:        $Header: /home/ghn/projects/PEAR/egg/eggsh/RCS/hwapi.h,v 1.1 1998/07/21 11:37:32 ghn Exp $
+ * PURPOSE:     Define interface to hardware layer
+ * AUTHOR:      Greg Nelson
+ * DATE:        98-04-12
+ *
+ * REVISED:     $Log: hwapi.h,v $
+ * REVISED:     Revision 1.1  1998/07/21 11:37:32  ghn
+ * REVISED:     Initial revision
+ * REVISED:
+ * Copyright 1998 - Greg Nelson
+ */
+
+typedef struct devopts {
+  uint16 type; /* 0 = PEAR */
+  uint16 port;
+  uint32 baud;
+} DevOpts;
+
+/* Open a device. 
+
+   DevOpts should contain any necessary information to determine how
+   this is to be done.  The return value, if >= 0, is a device handle.
+   Otherwise it indicated an error. */
+int32 OpenDev(DevOpts *opts);
+
+/* Evaluate speed.
+
+   dd is the device handle returned by opening the device.  The
+   function returns the estimated number of bits/second that the
+   device will produce, which should be used as a limit in setting the
+   data rate. */
+int32 EvalSpeed(int32 dd);
+
+/* Sample from a device.
+
+   dd is the device handle returned by opening the device.  The number
+   of bits requested will be sampled from the device immediately
+   (waiting as needed to collect the data, with no discards) and the
+   result will be returned. */
+int32 Sample(int32 dd, uint16 bits);
+
+/* Discard spare data from device.
+
+   dd is the device handle returned by opening the device.  The input
+   stream is consumed until a wait would be required.  The number of
+   bits discarded is returned. */
+int32 Discard(int32 dd);
+
+/* Close a device.
+
+   Closes a specified device handle. */
+int32 CloseDev(int32 dd);
diff --git a/ipproto.c b/ipproto.c
new file mode 100644 (file)
index 0000000..42b919c
--- /dev/null
+++ b/ipproto.c
@@ -0,0 +1,39 @@
+/* PROGRAM:     eggsh
+ * FILE:        $Header: /home/egg/src/RCS/ipproto.c,v 1.1 1998/07/21 11:37:21 ghn Exp $
+ * PURPOSE:     Declarations/definitions of IP data protocols for Egg
+ * AUTHOR:      Greg Nelson
+ * DATE:        98-04-12
+ *
+ * REVISED:     $Log: ipproto.c,v $
+ * REVISED:     Revision 1.1  1998/07/21 11:37:21  ghn
+ * REVISED:     Initial revision
+ * REVISED:
+ * Copyright 1998 - Greg Nelson
+ * Redistributable under the terms of the GNU Public Licence (GPL)
+ */
+
+#include <string.h>
+#include "global.h"
+#include "crc16.h"
+
+int32 PackPacket(char *buf, uint32 bufsize, EggCarton *cart) {
+  int32 rec, ntrl, llen;
+  uint16 crc;
+
+  if (bufsize < sizeof(EggHeader)) return -1;
+
+  memcpy(buf, &(cart->hdr), sizeof(EggHeader));
+  buf += sizeof(EggHeader);
+
+  ntrl = card->hdr.reclen;
+  llen = sizeof(uint32) + ntrl * sizeof(trial);
+
+  for (rec = 0; rec < cart->hdr.numrec; rec++) {
+    memcpy(buf, &(card->records[rec].timestamp), sizeof(uint32));
+    memcpy(buf + sizeof(uint32),
+          &(card->records[rec].trials), ntrl * sizeof(trial));
+    crc = BlockCRC16((byte *)buf, llen);
+    memcpy(buf + llen, &crc, sizeof(uint16));
+    buf += llen + sizeof(uint16);
+  }
+}
diff --git a/ipproto.h b/ipproto.h
new file mode 100644 (file)
index 0000000..4994a11
--- /dev/null
+++ b/ipproto.h
@@ -0,0 +1,17 @@
+/* PROGRAM:     eggsh
+ * FILE:        $Header: /home/ghn/projects/PEAR/egg/eggsh/RCS/ipproto.h,v 1.1 1998/07/21 11:37:06 ghn Exp $
+ * PURPOSE:     Declarations/definitions of IP data protocols for Egg
+ * AUTHOR:      Greg Nelson
+ * DATE:        98-04-12
+ *
+ * REVISED:     $Log: ipproto.h,v $
+ * REVISED:     Revision 1.1  1998/07/21 11:37:06  ghn
+ * REVISED:     Initial revision
+ * REVISED:
+ * Copyright 1998 - Greg Nelson
+ * Redistributable under the terms of the GNU Public Licence (GPL)
+ */
+
+#include "global.h"
+
+uint32 PackPacket(char *buf, uint32 bufsize, EggCarton *cart);
diff --git a/lecuyer.c b/lecuyer.c
new file mode 100644 (file)
index 0000000..2df4403
--- /dev/null
+++ b/lecuyer.c
@@ -0,0 +1,135 @@
+/*
+
+    L'Ecuyer's two-sequence pseudorandom generator with a 32 cell
+    Bays-Durham shuffle on the back end.
+
+    When updating the individual generators, an intermediate value as
+    large as 64 bits is required.  Many modern C compilers provide a
+    "long long" 64-bit integer type.  If this type is available,
+    compile this file with LONGLONG defined and it will be used,
+    generally improving execution speed.  If the compiler does not
+    implement "long long", compile without LONGLONG and Schrage's
+    algorithm will be used to perform the computation using only
+    32 bit arithmetic.
+
+    For additional details, see L'Ecuyer's paper:
+
+        L'Ecuyer, P. "Communications of the ACM", Vol. 31, p. 742 (1988).
+
+    and that of Bays and Durham:
+
+        Bays, Carter, and S.D. Durham.  "ACM Transactions on
+            Mathematical Software", Vol. 2, p. 59 (1976).
+
+    Schrage's multiple-precision modular algorithm is described in:
+
+        Schrage, P.  "ACM Transactions on Mathematical Software",
+           Vol. 5, p. 132 (1979).
+
+*/
+
+#include <stdio.h>
+#include <assert.h>
+
+#define LONGLONG
+
+#ifdef LONGLONG
+#define int64 long long
+#else
+#define int64 long
+#endif
+#define int32 long
+
+/* L'Ecuyer's recommended multiplier and modulus for the two
+   multiplicative congruential generators.  Even though the
+   values fit in 32 bits, we declare them as int64 so that the
+   arithmetic in calculating the next value will be automatically
+   done in int64 without need for casting when LONGLONG is defined.
+   In a non-LONGLONG build int64 is defined as long, so these values
+   participate correctly in the Schrage algorithm computation. */
+
+#define mul1 ((int64) 40014)
+#define mod1 ((int64) 2147483563)
+#define mul2 ((int64) 40692)
+#define mod2 ((int64) 2147483399)
+
+#define shuffleSize 32               /* Shuffle table size */
+#define warmup 19                     /* Number of initial warmup results to "burn" */
+
+static int32 gen1, gen2, state;
+static int32 shuffle[shuffleSize];    /* Bays-Durham shuffle table */
+
+/*  Update generator which, using either long long arithmetic or
+    Schrage's algorithm depending on whether LONGLONG is defined.
+    The definition for Schrage's algorithm relies, for efficiency,
+    on the C compiler performing compile-time constant arithmetic
+    for the quotient and remainder of the modulus and multiplier.
+    If you're porting this to a language which lacks that feature,
+    you'll want to predefine the quotient and remainder for each
+    generator and use the explicit values.  */
+
+#ifdef LONGLONG
+#define updgen(which)  gen##which = (int32) ((gen##which * mul##which) % mod##which)
+#else
+#define updgen(which) {                                                               \
+        int32 t = gen##which / (mod##which / mul##which);                             \
+                                                                                      \
+        gen##which = mul##which * (gen##which - (t * (mod##which / mul##which))) -    \
+                     t * ((mod##which % mul##which));                                 \
+        if (gen##which < 0) {                                                         \
+            gen##which += mod##which;                                                 \
+        }                                                                             \
+    }
+#endif
+
+/*  LEsetSeed  --  Set seed for generator.  Subsequent values will be based
+                  on the given nonzero seed.  */
+
+void LEsetSeed(int32 seed)
+{
+    int32 i;
+
+    assert(seed != 0);
+
+    gen1 = gen2 = (int32) (seed & 0x7FFFFFFFL);
+
+    /* "Warm up" the generator for a number of rounds to eliminate
+       any residual inflence of the seed. */
+
+    for (i = 0; i < warmup; i++) {
+       updgen(1);
+    }
+
+    /* Fill the shuffle table with values.  */
+
+    for (i = 0; i < shuffleSize; i++) {
+       updgen(1);
+       shuffle[(shuffleSize - 1) - i] = gen1;
+    }
+    state = shuffle[0];
+}
+
+/*  LEnextByte --  Get next byte from generator.  */
+
+unsigned char LEnextByte(void)
+{
+    int i;
+
+    updgen(1);
+    updgen(2);
+
+    /* Extract shuffle table index from most significant part
+       of the previous result. */
+
+    i = state / (1 + (((int32) mod1) - 1) / shuffleSize);
+
+    /* New state is sum of generators modulo one of their moduli.  */
+
+    state = (int32) (((shuffle[i]) + ((unsigned int32) gen2)) % mod1);
+
+    /* Replace value in shuffle table with generator 1 result. */
+
+    shuffle[i] = gen1;
+
+    return (unsigned char) (state / (1 + (((int32) mod1) - 1) / 256));
+}
diff --git a/lecuyer.h b/lecuyer.h
new file mode 100644 (file)
index 0000000..118c128
--- /dev/null
+++ b/lecuyer.h
@@ -0,0 +1,8 @@
+/*
+
+    Definitions for L'Ecuyer pseudorandom sequence generator.
+
+*/
+
+extern void LEsetSeed(long seed);
+extern unsigned char LEnextByte(void);
diff --git a/network.c b/network.c
new file mode 100644 (file)
index 0000000..b266358
--- /dev/null
+++ b/network.c
@@ -0,0 +1,368 @@
+/* PROGRAM:    eggsh
+ * FILE:       $Header: /home/egg/src/RCS/network.c,v 1.7 1999/02/28 20:14:20 ghn Exp $
+ * PURPOSE:    Network communication functions
+ * AUTHOR:     Greg Nelson
+ * DATE:       98-05-09
+ *
+ * REVISED:
+ * $Log: network.c,v $
+ * Revision 1.7  1999/02/28 20:14:20  ghn
+ * Version 5.1: Modified InitNetwork to take interface (addr) argument as
+ * well as port, and handle various cases of interface (NULL, IP,
+ * hostname) gracefully.  Modified NetUp to call this new version.
+ *
+ * Revision 1.6  1999/01/02 00:01:01  ghn
+ * Socket and sockaddr_in corrections suggested by Mike Cheponis
+ * incorporated.
+ * perror() turned off for ENETUNREACH/EHOSTUNREACH unless app specifies
+ * "gripe" argument to NetTalk.
+ *
+ * Revision 1.5  1998/12/31 22:07:56  ghn
+ * Rev 5 code: includes multi-reg support, HTML, etc.
+ *
+ * Revision 1.4  1998/08/03 20:39:03  kelvin
+ * inet_ntoa warning fix, bad packet diagnostics.
+ *
+ * Revision 1.3  1998/08/01  18:51:25  ghn
+ * Added John's byte-order-independence changes.
+ *
+ * Revision 1.2  1998/08/01 17:16:39  ghn
+ * Added DND support and John's typecasts.
+ *
+ * Revision 1.1  1998/07/21 11:36:45  ghn
+ * Initial revision
+ *
+ * Copyright 1998 - Greg Nelson
+ * Redistributable under the terms of the GNU Public Licence (GPL)
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <string.h>
+#include "global.h"
+#include "genlib.h"
+#include "network.h"
+#include "errnos.h"
+
+#define MAXBUFSIZE     512
+char           buffer[MAXBUFSIZE];
+
+/* Eventually, these might deal with encryption and decryption or
+   validation processes, but until such time as we actually do
+   something with these, they are no-ops. */
+#define PktEncrypt(buf, len)   ERR_NONE
+#define PktDecrypt(buf, len)   ERR_NONE
+
+#ifdef HEXDUMP
+extern void xd(FILE *out, void *bub, int bufl, int dochar);
+#endif
+
+int32 InitNetwork(char *addr, int32 port) {
+  struct protoent      *pp;
+  struct hostent       *hp;
+  struct sockaddr_in   sin;
+  struct utsname       uts;
+  int32                sd;
+  int32                argp;
+
+  if ((pp = getprotobyname("udp")) == NULL) {
+    perror("getprotobyname");
+    exit(-1);
+  }
+
+  /* Optimistically: */
+  memset(&sin, 0, sizeof(struct sockaddr_in));
+  sin.sin_port = htons(port);
+  sin.sin_family = AF_INET;
+
+  /* Try to convert dotted quad.  Hope this is okay for null addr? */
+  if (!addr || !inet_aton(addr, &(sin.sin_addr))) {
+    /* Nope, so try to gethostbyname */
+    if (!addr) {
+      /* No addr at all, use host name from uname */
+      if (uname(&uts) < 0) {
+       perror("uname");
+       exit(-1);
+      }
+    } else {
+      strcpy(uts.nodename, addr);
+    }
+    
+    if ((hp = gethostbyname(uts.nodename)) == NULL) {
+      fprintf(stderr, "gethostbyname(%s): %s", uts.nodename, strerror(errno));
+      exit(-1);
+    }
+
+    if (hp->h_addrtype != AF_INET) {
+      fprintf(stderr, "Host is not on the internet!\n");
+      exit(-1);
+    }
+
+    memcpy(&(sin.sin_addr), hp->h_addr, hp->h_length);
+  }
+
+  if ((sd = socket(PF_INET, SOCK_DGRAM, 0)) <= 0) {
+    printf("Could not make socket\n");
+    exit(-1);
+  }
+  
+  if (bind(sd, (struct sockaddr *)(&sin), sizeof(sin)) != 0) {
+    if (errno == EADDRNOTAVAIL) {
+      /* Network probably not up yet. */
+      close(sd);       /* Error pointed out my Mike Cheponis */
+      return ERR_NOREPLY;
+    }
+    perror("bind");
+    exit(-1);
+  }
+
+  argp = 1;
+#ifndef Solaris
+  if (ioctl(sd, FIONBIO, (char *)&argp) != 0) {
+    perror("ioctl FIONBIO");
+    exit(-1);
+  }
+#endif
+   
+  return sd;
+}
+
+/* Bring net up with command */
+int32 NetUp(char *cmd, char *addr, int32 port) {
+  int res;
+
+  res = system(cmd);
+  /* 0x7e00 = perm denied
+     0x7f00 = command not found
+     256 * cmd if command runs */
+  if (res != 0) return ERR_OTHER;
+
+  return InitNetwork(addr, port);
+}
+
+/* Bring net down with command */
+int NetDown(char *cmd, int32 oldsd) {
+  int res;
+
+  /* Close existing connection */
+  if (oldsd >= 0) close(oldsd);
+
+  res = system(cmd);
+  if (res != 0) return ERR_OTHER;
+
+  return ERR_NONE;
+}
+
+int NetGetAddr(struct sockaddr_in *sin, char *host, uint16 port) {
+  struct hostent       *hp;
+
+  if ((hp = gethostbyname(host)) == NULL) {
+    fprintf(stderr, "gethostbyname(%s): %s", host, strerror(errno));
+    return ERR_INRANGE;
+  }
+
+  if (hp->h_addrtype != AF_INET) {
+    fprintf(stderr, "Host is not on the internet!\n");
+    return ERR_INRANGE;
+  }
+
+  memset(sin, 0, sizeof(struct sockaddr_in));
+  sin->sin_port = htons(port);
+  sin->sin_family = AF_INET;
+  memcpy(&(sin->sin_addr), hp->h_addr, hp->h_length);
+  
+  return ERR_NONE;
+}
+
+/* Listen for a data request.
+
+   Listen on specified socket sd.  When one is received, validate the
+   checksum, and, if successful, allocate memory and stuff it as a
+   character array.  Return the remote sockaddr_in, if the sin pointer
+   is not null, and block until something interesting happens if block
+   is true. */
+int NetListen(int sd, char **pktbuf, 
+             struct sockaddr_in *sin,
+             int block) {
+  fd_set               fdset;
+  struct timeval       timeout, *top;
+  int32                nfound;
+  int32                size;
+  int32                count;
+  int32                res;
+  short                pktsize;
+  uint16               pkttype, cksumt, cksumc;
+
+  if (sd < 0) return ERR_INRANGE;
+
+  FD_ZERO(&fdset);
+  FD_SET(sd, &fdset);
+  timeout.tv_sec = timeout.tv_usec = 0;
+
+  /* Wait for an incoming connection.
+     If block is true, we wait indefinitely; otherwise, we
+     return immediately because of zero timeout. */
+
+  if (block) top = NULL; else top = &timeout;
+  nfound = select(FD_SETSIZE, &fdset, 0, 0, top);
+
+  if (nfound < 0) {
+    if (errno == EWOULDBLOCK) return ERR_COMM_TMOUT;
+    perror("select");
+    return ERR_OTHER;
+  }
+
+  /* No connections, go back to main loop. */
+  if (nfound == 0) return ERR_COMM_TMOUT;
+
+#ifdef DEBUG
+  fprintf(stderr, "Net port got a request!\n");
+#endif
+
+  if (!FD_ISSET(sd, &fdset)) {
+    fprintf(stderr, "Confused condition -- FD not part of set.\n");
+    return 0;
+  }
+
+  size = sizeof(*sin);
+  count = recvfrom(sd, buffer, MAXBUFSIZE, 0,
+                  (struct sockaddr *)sin, (int *)&size);
+  if (count < 0) {
+    /* Don't wait for it. */
+    if (errno == EWOULDBLOCK) return ERR_COMM_TMOUT;
+    perror("recvfrom");
+    return ERR_OTHER;
+  }
+
+#ifdef HEXDUMP
+  fprintf(stderr, "Received %ld bytes from %s\n", count,
+    inet_ntoa(sin->sin_addr));
+  xd(stderr, buffer, count, FALSE);
+#endif
+
+  /* Mangle buffer as needed */
+  if ((res = PktDecrypt(buffer, count)) < 0) {
+    fprintf(stderr, "Packet decryption failed with %d.\n", (int)res);
+    return res;
+  }
+
+  /* Verify length and CRC of packet.  In making these
+     protocol-level sanity checks, we transform the relevant
+     fields in the packet from network to host byte order
+     as required.  Note, however, that the packet returned
+     to the caller remains, in its entirety, in network
+     byte order and even references to protocol-common
+     fields, if made, must be converted by the code which
+     invokes this function. */
+
+  { uint16 rpkttype, rpktsize, rcrc;
+
+    memcpy(&rpkttype, buffer, sizeof rpkttype);
+    memcpy(&rpktsize, buffer + 2, sizeof rpktsize);
+    memcpy(&rcrc, buffer + (count - 2), sizeof rcrc);
+    pktsize = ntohs(rpktsize);
+    if (pktsize != count) {
+#ifdef DEBUG
+        fprintf(stderr, "** Bad packet length: pktsize = %d, count = %ld.\n",
+           pktsize, count);
+#endif
+       return ERR_PKT_BADLEN;
+    }
+    cksumt = ntohs(rcrc);
+    cksumc = BlockCRC16((byte *) buffer, count - 2);
+    if (cksumc != cksumt) {
+#ifdef DEBUG
+        fprintf(stderr, "** Bad packet CRC: packet = %04X, computed = %04X.\n",
+           cksumt, cksumc);
+#endif
+       return ERR_PKT_CKSUM;
+    }
+    pkttype = ntohs(rpkttype);
+  }
+
+#ifdef DEBUG
+  fprintf(stderr, "Recv packet type %04x, %d bytes (hdr), cks = %04x\n",
+         pkttype, pktsize, cksumt);
+#endif /* DEBUG */
+  
+  /* Hand back the packet */
+  *pktbuf = (char *)malloc(count);
+  memcpy(*pktbuf, buffer, count);
+
+  return ERR_NONE;
+}
+
+/* Send a data packet.
+
+   Checksum the packet, create a socket, and send it to the specified
+   port of the specified host. Length of packet to be extracted from
+   packet header. */
+
+  /* The entire contents of the packet passed to NetTalk must
+     be in network byte order. Fields within the packet used
+     to append the CRC, determine the number of bytes to send,
+     etc. are converted to host byte order as needed. */
+
+int NetTalk(struct sockaddr_in *sin, char *pkt, int gripe) {
+  uint16               pktsize, cksum;
+  static struct protoent *pp = NULL;
+  int                  i, out_sock;
+
+  memcpy(&pktsize, pkt + 2, 2);
+  pktsize = ntohs(pktsize);
+#ifdef DEBUG
+  if (pktsize > 1500) {
+    fprintf(stderr, "Bogus packet size of %ud bytes.  Suspect byte alignment bug.\n", pktsize);
+    abort();
+  }
+#endif
+  cksum = htons(BlockCRC16((byte *) pkt, pktsize - sizeof(uint16)));
+  memcpy(pkt+pktsize-sizeof(uint16), &cksum, sizeof(uint16));
+
+  if (pp == NULL) {                  /* Only get protocol code the first time */
+    if ((pp = getprotobyname("udp")) == NULL) {
+      perror("getprotobyname");
+      return ERR_OTHER;
+    }
+  }
+
+  if ((out_sock = socket(AF_INET, SOCK_DGRAM, pp->p_proto)) < 0) {
+    perror("socket");
+    return ERR_OTHER;
+  }
+       
+  i = sendto(out_sock, pkt, pktsize, 
+            0, (struct sockaddr *)sin,
+            sizeof(struct sockaddr));
+  if (i == -1) {
+    /* We only report on certain errors if told to "gripe", because
+       it is a normal occurence if in PERM mode with a variably
+       available net connection. */
+    if ((errno != ENETUNREACH && errno != EHOSTUNREACH) || gripe) perror("sendto");
+    close(out_sock);
+    return ERR_OTHER;
+  }
+
+#ifdef HEXDUMP
+  fprintf(stderr, "Sent %d bytes to %s\n", pktsize, inet_ntoa(sin->sin_addr));
+  xd(stderr, pkt, pktsize, FALSE);
+#endif
+
+  i = close(out_sock);
+  if (i < 0) {
+    perror("close");
+  }
+  
+  return ERR_NONE;
+}
diff --git a/network.h b/network.h
new file mode 100644 (file)
index 0000000..d8b8102
--- /dev/null
+++ b/network.h
@@ -0,0 +1,76 @@
+/* PROGRAM:     eggsh
+ * FILE:        $Header: /home/egg/src/RCS/network.h,v 1.6 1999/02/28 20:14:26 ghn Exp $
+ * PURPOSE:     Definitions for network communication functions
+ * AUTHOR:      Greg Nelson
+ * DATE:        98-05-09
+ *
+ * REVISED:
+ * $Log: network.h,v $
+ * Revision 1.6  1999/02/28 20:14:26  ghn
+ * Version 5.1: Modified InitNetwork to take interface (addr) argument as
+ * well as port, and handle various cases of interface (NULL, IP,
+ * hostname) gracefully.  Modified NetUp to call this new version.
+ *
+ * Revision 1.5  1999/01/02 00:02:24  ghn
+ * Modified NetTalk() api to offer "gripe" argument to silence some perror()s.
+ *
+ * Revision 1.4  1998/12/31 22:11:05  ghn
+ *  Rev 5 code: includes multi-reg support, HTML, etc.
+ *
+ * Revision 1.3  1998/08/01 18:51:25  ghn
+ * Added John's byte-order-independence changes.
+ *
+ * Revision 1.2  1998/08/01 17:17:40  ghn
+ * Added DND support.
+ *
+ * Revision 1.1  1998/07/21 11:36:34  ghn
+ * Initial revision
+ *
+ * Copyright 1998 - Greg Nelson
+ * Redistributable under the terms of the GNU Public Licence (GPL)
+ */
+
+#ifndef _NETWORK_H
+#define _NETWORK_H
+
+#include <netinet/in.h>
+
+#ifdef TESTPORT
+#define EGGPORT         2074
+#define BASKETPORT      2075
+#else
+#define        EGGPORT         1105
+#define        BASKETPORT      2510
+#endif
+
+/* Initialize network subsystem, returning socket descriptor */
+extern int32 InitNetwork(char *addr, int32 port);
+
+/* Bring net up with command */
+extern int32 NetUp(char *cmd, char *addr, int32 port);
+
+/* Bring net down with command */
+extern int NetDown(char *cmd, int32 oldsd);
+
+/* Get a sockaddr given a host and port */
+extern int NetGetAddr(struct sockaddr_in *sin, char *host, uint16 port);
+
+/* Listen for a data request.
+
+   Listen on specified socket sd.  When one is received, validate the
+   checksum, and, if successful, allocate memory and stuff it as a
+   character array.  Return the remote sockaddr_in, if the sin pointer
+   is not null, and block until something interesting happens if block
+   is true. */
+extern int NetListen(int sd, char **pktbuf, 
+                    struct sockaddr_in *sin,
+                    int block);
+
+/* Send a data packet.
+
+   Create a socket, checksum the packet, and send it to the specified
+   addr in sin.  Length of packet to be extracted from packet
+   header. */
+extern int NetTalk(struct sockaddr_in *sin, char *pkt, int gripe);
+
+#endif /* !_NETWORK_H */
diff --git a/reg.h b/reg.h
new file mode 100644 (file)
index 0000000..608fdbb
--- /dev/null
+++ b/reg.h
@@ -0,0 +1,76 @@
+/*
+
+       Random event generator definitions
+
+       This file contains definitions common to all random
+       event generators.  All implement the interface defined
+       here.
+
+*/
+
+
+typedef struct {
+    uint16 type;                     /* Device type code (allows supporting
+                                        multiple devices with common driver
+                                        code). */
+    uint16 port;                     /* Port ID.  Meaning depends on nature
+                                        of hardware, and may mean nothing at
+                                        all. */
+    uint32 baud;                     /* Baud rate for devices connected to
+                                        a serial port.  This may have a
+                                        different meaning for devices
+                                        connected to, for example, a
+                                        parallel port or SCSI string. */
+} DevOpts;
+
+typedef struct {
+    char *reg_name;                  /* Name of REG, corresponding to
+                                        the name on the REG statement
+                                        in .eggrc */
+    uint16 reg_type;                 /* REG type code, corresponding
+                                        to type field in the DevOpts
+                                        structure. */
+
+    /* The following functions are used to call the methods
+       of the REG device driver. */
+
+    /* Open a device. 
+
+       DevOpts should contain any necessary information to determine how
+       this is to be done.  The return value, if >= 0, is a device handle.
+       Otherwise it indicates an error. */
+
+    int32 (*reg_OpenDev)(DevOpts *opts);
+
+    /* Evaluate speed.
+
+       dd is the device handle returned by opening the device. The
+       function returns the estimated number of bits/second that the
+       device will produce, which should be used as a limit in setting the
+       data rate. */
+
+    int32 (*reg_EvalSpeed)(int32 dd);
+
+    /* Sample from a device.
+
+       dd is the device handle returned by opening the device. The number
+       of bits requested will be sampled from the device immediately
+       (waiting as needed to collect the data, with no discards) and the
+       result will be returned. */
+
+    int32 (*reg_Sample)(int32 dd, uint16 bits);
+
+    /* Discard spare data from device.
+
+       dd is the device handle returned by opening the device. The input
+       stream is consumed until a wait would be required.  The number of
+       bits discarded is returned. */
+
+    int32 (*reg_Discard)(int32 dd);
+
+    /* Close a device.
+
+       Closes a specified device handle. */
+
+    int32 (*reg_CloseDev)(int32 dd);
+} REG_driver;
diff --git a/reg_orion.c b/reg_orion.c
new file mode 100644 (file)
index 0000000..d6938ef
--- /dev/null
@@ -0,0 +1,306 @@
+/* PROGRAM:    eggsh
+ * FILE:       $Header: /home/egg/src/RCS/reg_orion.c,v 1.1 1998/12/31 22:04:39 ghn Exp $
+ * PURPOSE:    PEAR (Bradish box/micro-REG) hardware interface
+ * AUTHOR:     Greg Nelson
+ * DATE:       98-04-12
+ *
+ * REVISED:    $Log: reg_orion.c,v $
+ * REVISED:    Revision 1.1  1998/12/31 22:04:39  ghn
+ * REVISED:    Initial revision
+ * REVISED:
+ * REVISED:    Revision 1.3  1998/08/01 18:50:39  ghn
+ * REVISED:     Added John's byte-order-independence changes and PSEUDO support.
+ * REVISED:
+ * REVISED:    Revision 1.2  1998/08/01 17:13:51  ghn
+ * REVISED:     Added John's Solaris support and DUMPREG option.
+ * REVISED:
+ * REVISED:    Revision 1.1  1998/07/21 11:37:41  ghn
+ * REVISED:    Initial revision
+ * REVISED:
+ * Copyright 1998 - Greg Nelson
+ * Redistributable under the terms of the GNU Public Licence (GPL)
+ */
+
+/* Define this to dump samples from the REG which we actually use into
+   a file named dumpreg.dat.  This can be used to debug serial port
+   mode problems (for example, setting the character frame to 7 bits,
+   or telling it to ignore breaks, which will lose all zero bytes. */
+/* #define DUMPREG */
+
+#include <stdio.h>
+#ifdef __USE_BSD
+#undef __USE_BSD
+#endif
+#define __USE_BSD
+#include <termios.h>
+#undef __USE_BSD
+#include <unistd.h>
+#include <fcntl.h>
+#define __USE_BSD
+#include <errno.h>
+#undef __USE_BSD
+
+#include "global.h"
+#include "genlib.h"
+#include "reg.h"
+
+#define MAXDEV 20
+
+/*  The Orion REG is powered by the serial port into which
+    it is plugged.  If the port is not providing power to
+    the REG before we initialise it, the REG may deliver
+    garbage data as the power comes up.  To guard against
+    this we take two precautions.  First of all, we sleep
+    REG_INIT seconds after initialising the serial port to
+    let the REG start up and stabilise after power is
+    turned on, and we further discard REG_DISCARD initial
+    samples which may have been queued as the REG was
+    powering up.  If either of these symbols is not defined
+    or zero, the corresponding start-up action will not
+    be taken.  */
+
+#define REG_INIT       10            /* REG power on delay */
+#define REG_DISCARD    1024          /* Bytes to discard at start-up */
+
+static int32 oldbits[MAXDEV], bitsleft[MAXDEV];
+
+#ifdef DUMPREG
+static FILE *dumpfile;               /* REG dump file handle */
+static unsigned char dumpbuf[1024];   /* REG dump buffer */
+static int dumpptr = 0;              /* Pointer into dump buffer */
+#endif
+
+#ifdef FLUSH
+extern int flush_fd;                 /* File descriptor for auto-flush */
+#endif
+
+static int32 OpenDev(DevOpts *opts) {
+  char ttydev[30];
+  speed_t baudcon;
+  struct termios tt;
+  int32 TTY_fd, res;
+
+#ifdef DUMPREG
+  dumpfile = fopen("dumpreg.dat", "w");
+  setbuf(dumpfile, NULL);
+  dumpptr = 0;
+#endif
+
+#ifdef Solaris
+  /* Serial ports (at least built-in ones) have names of
+     /dev/term/a, /dev/term/b, etc.  Map the port numbers
+     from the RC file into this nomenclature, with "1"
+     designating /dev/term/a. */
+#ifdef Irix
+  sprintf(ttydev, "/dev/ttyd%d", opts->port);
+#else
+  sprintf(ttydev, "/dev/term/%c", 'a' + (opts->port - 1));
+#endif
+#else
+  sprintf(ttydev, "/dev/ttyS%d", opts->port);
+#endif
+
+  switch(opts->baud) {
+  case 1200: baudcon = B1200; break;
+  case 2400: baudcon = B2400; break;
+  case 4800: baudcon = B4800; break;
+  case 9600: baudcon = B9600; break;
+  case 19200: baudcon = B19200; break;
+  case 38400: baudcon = B38400; break;
+#ifndef Irix
+  case 115200: baudcon = B115200; break;
+#endif
+  default:
+    printf("%s: Baud rate %ld not supported.\n", pgmname, opts->baud);
+    return -1;
+  }
+
+  fprintf(stderr, "Opening %s at %ld\n", ttydev, opts->baud);
+  if ((TTY_fd = open(ttydev, O_RDONLY
+#ifdef CPU_BOUND
+                                   | O_NDELAY
+#endif
+      )) < 0) {
+    perror(pgmname);
+fprintf(stderr, "Error in open(%s)\n", ttydev);
+    return -1;
+  }
+
+  if (TTY_fd >= MAXDEV) {
+    fprintf(stderr, "%s: Too many devices open.\n", pgmname);
+    close(TTY_fd);
+    return -1;
+  }
+
+  res = tcgetattr(TTY_fd, &tt);
+#ifdef Solaris
+  tt.c_iflag = 0;
+  tt.c_oflag = 0;
+#ifdef SWITCH_READ
+  tt.c_cflag = baudcon | CS8 | CLOCAL;
+#else
+  tt.c_cflag = baudcon | CS8 | CREAD | CLOCAL;
+#endif
+  tt.c_lflag = 0;
+  tt.c_cc[VMIN] = 1;   /* This many chars satisfies reads */
+  tt.c_cc[VTIME] = 0;  /* or in this many tenths of seconds */
+#else
+  res = cfsetospeed(&tt, baudcon); 
+  cfmakeraw(&tt);
+  tt.c_oflag &= (~(TABDLY | ONLCR));
+#endif
+  res = tcsetattr(TTY_fd, TCSANOW, &tt);
+
+  oldbits[TTY_fd] = bitsleft[TTY_fd] = 0;
+
+  /* Perform start-up power on delay and data flush. */
+
+#ifdef REG_INIT
+  if (REG_INIT > 0) {
+    sleep(REG_INIT);
+  }
+#endif
+
+#ifdef REG_DISCARD
+  if (REG_DISCARD > 0) {
+    int d, n1;
+    char c1;
+
+    for (d = 0; d < REG_DISCARD; d++) {
+      do {
+       n1 = read(TTY_fd, &c1, 1);
+      } while (n1 == 0 || (n1 == -1 && errno == EAGAIN));
+    }
+  }
+#endif
+
+#ifdef FLUSH
+  flush_fd = TTY_fd;
+#endif
+
+  return TTY_fd;
+}
+
+static int32 Sample(int32 dd, uint16 bits) {
+  int32 bc, sum, n1;
+  uint8 c1;
+
+#ifdef Solaris
+#ifdef SWITCH_READ
+  struct termios tt;
+
+  tcgetattr(dd, &tt);
+  tt.c_cflag |= CREAD;
+  tcsetattr(dd, TCSANOW, &tt);
+#endif
+#endif
+
+  if (dd < 0) return -1;
+
+  sum = bc = 0;
+  while (bc < bits) {
+    if (bitsleft[dd]) {
+      sum += (oldbits[dd] & 0x01);
+      oldbits[dd] >>= 1;
+      bitsleft[dd]--;
+      bc++;
+    } else {
+      do {
+       n1 = read(dd, &c1, 1);
+      } while (n1 == 0 || (n1 == -1 && errno == EAGAIN));
+      if (n1 == -1) {
+       /* Fatal error occurred, die now? */
+       return -1;
+      }
+
+      /* Orion REG does not compensate for first order bias.  XOR
+        each sample with 01010101b to invert the sense of
+        alternate bits in the samples. */
+
+      c1 ^= 0x55;
+#ifdef DUMPREG
+      dumpbuf[dumpptr++] = c1;
+      if (dumpptr >= sizeof(dumpbuf)) {
+       fwrite(dumpbuf, sizeof(dumpbuf), 1, dumpfile);
+       dumpptr = 0;
+      }
+#endif
+      oldbits[dd] = c1;
+      bitsleft[dd] = 8;
+    }
+  }
+
+#ifdef Solaris
+#ifdef SWITCH_READ
+  tcgetattr(dd, &tt);
+  tt.c_cflag &= ~CREAD;
+  tcsetattr(dd, TCSANOW, &tt);
+#endif
+#endif
+  return sum;
+}
+
+#define SAMP_PERIOD    1000    /* msec */
+#define MARGIN         .95     /* how much to headroom to allow in
+                                  speed measurement */
+
+static int32 EvalSpeed(int32 dd) {
+  struct timeval start, end;
+  int32 bitct, samp;
+
+  if (dd < 0) return -1;
+
+  gettimeofday(&start, NULL);
+  bitct = 0;
+  while (1) {
+    gettimeofday(&end, NULL);
+    if (deltams(&end, &start) >= SAMP_PERIOD) break;
+    samp = Sample(dd, 1);
+    bitct++;
+  }
+
+  return (int32)(bitct * MARGIN);
+}
+
+static int32 Discard(int32 dd) {
+  int32 disc;
+#ifdef CPU_BOUND
+  int32  n1;
+  uint8 c1;
+#endif
+  
+  if (dd < 0) return -1;
+
+  disc = bitsleft[dd];
+  bitsleft[dd] = 0;
+
+#ifdef CPU_BOUND
+  do {
+    n1 = read(dd, &c1, 1);
+    if (n1 == 1) disc++;
+  } while (n1 == 1);
+#else
+  tcflush(dd, TCIFLUSH);
+#endif
+
+  return disc;
+}
+
+static int32 CloseDev(int32 dd) {
+  if (dd < 0) return -1;
+
+  close(dd);
+  return 0;
+}
+
+/*  Driver description table.  */
+
+REG_driver REG_orion = {
+                        "ORION", 
+                       1000,
+                       OpenDev,
+                       EvalSpeed,
+                       Sample,
+                       Discard,
+                       CloseDev
+                     };
diff --git a/reg_pear.c b/reg_pear.c
new file mode 100644 (file)
index 0000000..bc79bb7
--- /dev/null
@@ -0,0 +1,232 @@
+/* PROGRAM:    eggsh
+ * FILE:       $Header: /home/egg/src/RCS/reg_pear.c,v 1.1 1998/12/31 22:04:39 ghn Exp $
+ * PURPOSE:    PEAR (Bradish box/micro-REG) hardware interface
+ * AUTHOR:     Greg Nelson
+ * DATE:       98-04-12
+ *
+ * REVISED:    $Log: reg_pear.c,v $
+ * REVISED:    Revision 1.1  1998/12/31 22:04:39  ghn
+ * REVISED:    Initial revision
+ * REVISED:
+ * REVISED:    Revision 1.3  1998/08/01 18:50:39  ghn
+ * REVISED:     Added John's byte-order-independence changes and PSEUDO support.
+ * REVISED:
+ * REVISED:    Revision 1.2  1998/08/01 17:13:51  ghn
+ * REVISED:     Added John's Solaris support and DUMPREG option.
+ * REVISED:
+ * REVISED:    Revision 1.1  1998/07/21 11:37:41  ghn
+ * REVISED:    Initial revision
+ * REVISED:
+ * Copyright 1998 - Greg Nelson
+ * Redistributable under the terms of the GNU Public Licence (GPL)
+ */
+
+/* Define this to dump samples from the REG which we actually use into
+   a file named dumpreg.dat.  This can be used to debug serial port
+   mode problems (for example, setting the character frame to 7 bits,
+   or telling it to ignore breaks, which will lose all zero bytes. */
+/* #define DUMPREG */
+
+#include <stdio.h>
+#ifdef __USE_BSD
+#undef __USE_BSD
+#endif
+#define __USE_BSD
+#include <termios.h>
+#undef __USE_BSD
+#include <unistd.h>
+#include <fcntl.h>
+#define __USE_BSD
+#include <errno.h>
+#undef __USE_BSD
+
+#include "global.h"
+#include "genlib.h"
+#include "reg.h"
+
+#define MAXDEV 20
+
+static int32 oldbits[MAXDEV], bitsleft[MAXDEV];
+
+#ifdef DUMPREG
+static FILE *dumpfile;               /* REG dump file handle */
+static unsigned char dumpbuf[1024];   /* REG dump buffer */
+static int dumpptr = 0;              /* Pointer into dump buffer */
+#endif
+
+static int32 OpenDev(DevOpts *opts) {
+  char ttydev[30];
+  speed_t baudcon;
+  struct termios tt;
+  int32 TTY_fd, res;
+
+#ifdef DUMPREG
+  dumpfile = fopen("dumpreg.dat", "w");
+  setbuf(dumpfile, NULL);
+  dumpptr = 0;
+#endif
+
+#ifdef Solaris
+  /* Serial ports (at least built-in ones) have names of
+     /dev/term/a, /dev/term/b, etc.  Map the port numbers
+     from the RC file into this nomenclature, with "1"
+     designating /dev/term/a. */
+#ifdef Irix
+  sprintf(ttydev, "/dev/ttyd%d", opts->port);
+#else
+  sprintf(ttydev, "/dev/term/%c", 'a' + (opts->port - 1));
+#endif
+#else
+  sprintf(ttydev, "/dev/ttyS%d", opts->port);
+#endif
+
+  switch(opts->baud) {
+  case 1200: baudcon = B1200; break;
+  case 2400: baudcon = B2400; break;
+  case 4800: baudcon = B4800; break;
+  case 9600: baudcon = B9600; break;
+  case 19200: baudcon = B19200; break;
+  case 38400: baudcon = B38400; break;
+#ifndef Irix
+  case 115200: baudcon = B115200; break;
+#endif
+  default:
+    printf("%s: Baud rate %ld not supported.\n", pgmname, opts->baud);
+    return -1;
+  }
+
+  fprintf(stderr, "Opening %s at %ld\n", ttydev, opts->baud);
+  if ((TTY_fd = open(ttydev, O_RDONLY
+#ifdef CPU_BOUND
+                                   | O_NDELAY
+#endif
+      )) < 0) {
+    perror(pgmname);
+    return -1;
+  }
+
+  if (TTY_fd >= MAXDEV) {
+    fprintf(stderr, "%s: Too many devices open.\n", pgmname);
+    close(TTY_fd);
+    return -1;
+  }
+
+  res = tcgetattr(TTY_fd, &tt);
+#ifdef Solaris
+  tt.c_iflag = 0;
+  tt.c_oflag = 0;
+  tt.c_cflag = baudcon | CS8 | CREAD | CLOCAL;
+  tt.c_lflag = 0;
+  tt.c_cc[VMIN] = 1;   /* This many chars satisfies reads */
+  tt.c_cc[VTIME] = 0;  /* or in this many tenths of seconds */
+#else
+  res = cfsetospeed(&tt, baudcon); 
+  cfmakeraw(&tt);
+  tt.c_oflag &= (~(TABDLY | ONLCR));
+#endif
+  res = tcsetattr(TTY_fd, TCSANOW, &tt);
+
+  oldbits[TTY_fd] = bitsleft[TTY_fd] = 0;
+  return TTY_fd;
+}
+
+static int32 Sample(int32 dd, uint16 bits) {
+  int32 bc, sum, n1;
+  uint8 c1;
+
+  if (dd < 0) return -1;
+
+  sum = bc = 0;
+  while (bc < bits) {
+    if (bitsleft[dd]) {
+      sum += (oldbits[dd] & 0x01);
+      oldbits[dd] >>= 1;
+      bitsleft[dd]--;
+      bc++;
+    } else {
+      do {
+       n1 = read(dd, &c1, 1);
+      } while (n1 == 0 || (n1 == -1 && errno == EAGAIN));
+      if (n1 == -1) {
+       /* Fatal error occurred, die now? */
+       return -1;
+      }
+#ifdef DUMPREG
+      dumpbuf[dumpptr++] = c1;
+      if (dumpptr >= sizeof(dumpbuf)) {
+       fwrite(dumpbuf, sizeof(dumpbuf), 1, dumpfile);
+       dumpptr = 0;
+      }
+#endif
+      oldbits[dd] = c1;
+      bitsleft[dd] = 8;
+    }
+  }
+
+  return sum;
+}
+
+#define SAMP_PERIOD    1000    /* msec */
+#define MARGIN         .95     /* how much to headroom to allow in
+                                  speed measurement */
+
+static int32 EvalSpeed(int32 dd) {
+  struct timeval start, end;
+  int32 bitct, samp;
+
+  if (dd < 0) return -1;
+
+  gettimeofday(&start, NULL);
+  bitct = 0;
+  while (1) {
+    gettimeofday(&end, NULL);
+    if (deltams(&end, &start) >= SAMP_PERIOD) break;
+    samp = Sample(dd, 1);
+    bitct++;
+  }
+
+  return (int32)(bitct * MARGIN);
+}
+
+static int32 Discard(int32 dd) {
+  int32 disc;
+#ifdef CPU_BOUND
+  int32  n1;
+  uint8 c1;
+#endif
+  
+  if (dd < 0) return -1;
+
+  disc = bitsleft[dd];
+  bitsleft[dd] = 0;
+
+#ifdef CPU_BOUND
+  do {
+    n1 = read(dd, &c1, 1);
+    if (n1 == 1) disc++;
+  } while (n1 == 1);
+#else
+  tcflush(dd, TCIFLUSH);
+#endif
+
+  return disc;
+}
+
+static int32 CloseDev(int32 dd) {
+  if (dd < 0) return -1;
+
+  close(dd);
+  return 0;
+}
+
+/*  Driver description table.  */
+
+REG_driver REG_pear = {
+                        "PEAR", 
+                       0,
+                       OpenDev,
+                       EvalSpeed,
+                       Sample,
+                       Discard,
+                       CloseDev
+                     };
diff --git a/reg_pseudo.c b/reg_pseudo.c
new file mode 100644 (file)
index 0000000..bd35883
--- /dev/null
@@ -0,0 +1,134 @@
+/* PROGRAM:     eggsh
+ * FILE:        $Header: /home/egg/src/RCS/reg_pseudo.c,v 1.1 1998/12/31 22:04:39 ghn Exp $
+ * PURPOSE:     PEAR (Bradish box/micro-REG) hardware interface
+ * AUTHOR:      Greg Nelson
+ * DATE:        98-04-12
+ *
+ * REVISED:     $Log: reg_pseudo.c,v $
+ * REVISED:     Revision 1.1  1998/12/31 22:04:39  ghn
+ * REVISED:     Initial revision
+ * REVISED:
+ * REVISED:     Revision 1.3  1998/08/01 18:50:39  ghn
+ * REVISED:     Added John's byte-order-independence changes and PSEUDO support.
+ * REVISED:
+ * REVISED:     Revision 1.2  1998/08/01 17:13:51  ghn
+ * REVISED:     Added John's Solaris support and DUMPREG option.
+ * REVISED:
+ * REVISED:     Revision 1.1  1998/07/21 11:37:41  ghn
+ * REVISED:     Initial revision
+ * REVISED:
+ * Copyright 1998 - Greg Nelson
+ * Redistributable under the terms of the GNU Public Licence (GPL)
+ */
+
+/* Define this to dump samples from the REG which we actually use into
+   a file named dumpreg.dat.  */
+/* #define DUMPREG */
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "global.h"
+#include "genlib.h"
+#include "reg.h"
+#include "lecuyer.h"
+
+#define MAXDEV  20
+
+static int32 oldbits[MAXDEV], bitsleft[MAXDEV];
+
+#ifdef DUMPREG
+static FILE *dumpfile;                /* REG dump file handle */
+static unsigned char dumpbuf[1024];   /* REG dump buffer */
+static int dumpptr = 0;               /* Pointer into dump buffer */
+#endif
+
+static int32 OpenDev(DevOpts *opts) {
+    int32 seed;
+    int i;
+
+    seed = time(NULL);
+    LEsetSeed(seed);
+
+    for (i = 0; i < 37; i++) {
+        seed = (seed << 3) ^ LEnextByte();
+    }
+    LEsetSeed(seed);
+    seed = LEnextByte();
+    for (i = 0; i < (seed & 0x37); i++) {
+        (void) LEnextByte();
+    }
+    return 0;
+}
+
+static int32 Sample(int32 dd, uint16 bits) {
+  int32 bc, sum;
+  uint8 c1;
+
+  sum = bc = 0;
+  while (bc < bits) {
+    if (bitsleft[dd]) {
+      sum += (oldbits[dd] & 0x01);
+      oldbits[dd] >>= 1;
+      bitsleft[dd]--;
+      bc++;
+    } else {
+      c1 = LEnextByte();
+#ifdef DUMPREG
+      dumpbuf[dumpptr++] = c1;
+      if (dumpptr >= sizeof(dumpbuf)) {
+        fwrite(dumpbuf, sizeof(dumpbuf), 1, dumpfile);
+        dumpptr = 0;
+      }
+#endif
+      oldbits[dd] = c1;
+      bitsleft[dd] = 8;
+    }
+  }
+
+  return sum;
+}
+
+#define SAMP_PERIOD     1000    /* msec */
+#define MARGIN          .95     /* how much to headroom to allow in
+                                   speed measurement */
+
+static int32 EvalSpeed(int32 dd) {
+  struct timeval start, end;
+  int32 bitct, samp;
+
+  gettimeofday(&start, NULL);
+  bitct = 0;
+  while (1) {
+    gettimeofday(&end, NULL);
+    if (deltams(&end, &start) >= SAMP_PERIOD) break;
+    samp = Sample(dd, 1);
+    bitct++;
+  }
+
+  return (int32)(bitct * MARGIN);
+}
+
+static int32 Discard(int32 dd) {
+  int32 disc;
+  
+  disc = bitsleft[dd];
+  bitsleft[dd] = 0;
+  return disc;
+}
+
+static int32 CloseDev(int32 dd) {
+  return 0;
+}
+
+/*  Driver description table.  */
+
+REG_driver REG_pseudo = {
+                        "PSEUDO", 
+                        2000,
+                        OpenDev,
+                        EvalSpeed,
+                        Sample,
+                        Discard,
+                        CloseDev
+                      };
diff --git a/regs.h b/regs.h
new file mode 100644 (file)
index 0000000..8222ddd
--- /dev/null
+++ b/regs.h
@@ -0,0 +1,17 @@
+/*
+
+    Random event generator driver interface.
+
+*/
+
+#include "reg.h"
+
+extern REG_driver *configuredREG;     /* REG selected by .eggrc file */
+
+/*  Access to methods within the configured REG driver.  */
+
+#define OpenDev(opts) (*configuredREG->reg_OpenDev)(opts)
+#define EvalSpeed(dd) (*configuredREG->reg_EvalSpeed)(dd)
+#define Sample(dd, bits) (*configuredREG->reg_Sample)(dd, bits)
+#define Discard(dd) (*configuredREG->reg_Discard)(dd)
+#define CloseDev(dd) (*configuredREG->reg_CloseDev)(dd)
diff --git a/regtable.h b/regtable.h
new file mode 100644 (file)
index 0000000..b8ad192
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+
+    Table of available Random Event Generator drivers
+
+*/
+
+#define REGs(x)    x##pear, x##pseudo, x##orion
+
+extern REG_driver REGs(REG_);
+
+static REG_driver *reg_table[] = {
+    REGs(&REG_), NULL
+};
diff --git a/regtest.c b/regtest.c
new file mode 100644 (file)
index 0000000..e4b5123
--- /dev/null
+++ b/regtest.c
@@ -0,0 +1,120 @@
+#include <stdio.h>
+#ifdef __USE_BSD
+#undef __USE_BSD
+#endif
+#define __USE_BSD
+#include <termios.h>
+#undef __USE_BSD
+#include <unistd.h>
+#include <fcntl.h>
+#define __USE_BSD
+#include <errno.h>
+#undef __USE_BSD
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <string.h>
+#include <utmp.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <syslog.h>
+#include "paths.h"
+
+int Sample(int fd, int bits);
+
+int main(int argc, char *argv[]) {
+  int TTY_fd;
+  int i, res, n1, mean;
+  struct termios tt;
+  speed_t baud;
+
+  if (argc != 3) {
+    printf("Usage: %s <tty> <baud>\n", argv[0]);
+    exit(-1);
+  }
+
+  switch(atoi(argv[2])) {
+  case 1200: baud = B1200; break;
+  case 2400: baud = B2400; break;
+  case 4800: baud = B4800; break;
+  case 9600: baud = B9600; break;
+  case 19200: baud = B19200; break;
+  case 38400: baud = B38400; break;
+  case 115200: baud = B115200; break;
+  default:
+    printf("%s: Baud rate %s not supported.\n", argv[0], argv[2]);
+    exit(-1);
+  }
+
+  if ((TTY_fd = open(argv[1], O_RDWR | O_NDELAY)) < 0) {
+    fprintf(stderr, "%s: %s\n", argv[1], sys_errlist[errno]);
+    exit(1);
+  }
+
+  res = tcgetattr(TTY_fd, &tt);
+  res = cfsetospeed(&tt, baud); 
+  cfmakeraw(&tt);
+  tt.c_oflag &= (~(TABDLY | ONLCR));
+  res = tcsetattr(TTY_fd, TCSANOW, &tt);
+
+  mean = 0;
+  for (i = 0; i < 100; i++) {
+    n1 = Sample(TTY_fd, 200);
+    printf("%3d ", n1);
+    fflush(stdout);
+    if (i % 20 == 19) printf("\n");
+    mean += n1;
+  }
+  printf("Mean of first 100 REG200 trials = %f\n", (double)mean / 100.0);
+
+  exit(0);
+  for (i = 100; i < 200; i++) {
+    n1 = Sample(TTY_fd, i);
+    printf("%3d: %3d\n", i, n1);
+  }
+  return 0;
+}
+
+static int oldbits = 0;
+static int bitsleft = 0;
+int Sample(int fd, int bits) {
+  int bc, sum, n1;
+  unsigned char c1;
+
+  sum = bc = 0;
+  while (bc < bits) {
+    if (bitsleft) {
+      sum += (oldbits & 0x01);
+      oldbits >>= 1;
+      bitsleft--;
+      bc++;
+    } else {
+      do {
+       n1 = read(fd, &c1, 1);
+      } while (n1 == 0 || (n1 == -1 && errno == EAGAIN));
+      if (n1 == -1) {
+       /* Fatal error occurred, die now? */
+       exit(-errno);
+      }
+      oldbits = c1;
+      bitsleft = 8;
+#if 0
+      printf("Sampled %c%c%c%c%c%c%c%c\n",
+            ((oldbits & 0x80)?'1':'0'),
+            ((oldbits & 0x40)?'1':'0'),
+            ((oldbits & 0x20)?'1':'0'),
+            ((oldbits & 0x10)?'1':'0'),
+            ((oldbits & 0x08)?'1':'0'),
+            ((oldbits & 0x04)?'1':'0'),
+            ((oldbits & 0x02)?'1':'0'),
+            ((oldbits & 0x01)?'1':'0'));
+#endif
+    }
+  }
+
+  return sum;
+}
+
+  
diff --git a/sample.basketrc b/sample.basketrc
new file mode 100644 (file)
index 0000000..09da1e6
--- /dev/null
@@ -0,0 +1,10 @@
+#BASKET diesse 193.8.230.134
+#BASKET jura 193.8.230.130
+BASKET noosphere 128.112.35.133
+EGG diesse 37 193.8.230.134 diesse PERM 1 http://www.fourmilab.ch/
+EGG noosphere 28 128.112.35.133 noosphere PERM 1 http://noosphere.princeton.edu/
+EGG throop 1003 193.8.230.132 throop PERM 1
+EGG jura 1004 193.8.230.130 jura PERM 1
+EGG dicklnx.psy.uva.nl 1000 145.18.117.41 diesse PERM 1 http://www.psy.uva.nl/bierman
+PROTOCOL 10 10 6 200
+HTML 60 /ftp/entrenous/k/basket.html
diff --git a/sample.eggrc b/sample.eggrc
new file mode 100644 (file)
index 0000000..b8c44bc
--- /dev/null
@@ -0,0 +1,86 @@
+# Configuration file for egg collection software
+# This specifies the egg configuration information, the contact
+# information for its basket(s), and initial data acquisition parameters.
+
+# Each line consists of a case-sensitive keyword and a series of
+# options.  Defined keywords are: 
+#
+#   EGG <name> <id> <ip addr> <primbasket> <conntype> <connival> <url>
+#
+# The primary basket <primbasket> is the name of the basket (defined
+# below) that should be contacted retrieve data from this Egg.
+# The connection type <conntype> is "PERM" (permanent) or "DND"
+# (dial-and-drop).  So far the code does not support the DND option.
+# The connection interval <connival> determines the time (in minutes)
+# between transmission attempts.  If specified, and not equal to
+# ".", <url> gives a URL (for example http://www.somesite.net/page.html)
+# which will be linked to the egg <name> in status reports.  Note that
+# this specification has meaning only in the .basketrc file.  For
+# compatibility, it is permitted in an .eggrc file, but is ignored.
+#
+#   BASKET <name> <ip addr>
+#
+# This entry tells us about the existence of a set of baskets.  The
+# basket named as primary is the one which will be contacted
+# initially. 
+#
+#   PROTOCOL <samprec> <secrec> <recpkt> <trialsz>
+#
+# Specify the default data collection protocol.  The arguments are 
+# <samprec> samples per record (1-10, though this should always be 5-10),
+# <secrec> seconds per records (1-3000),
+# <recpkt> records per packet (1-60), and 
+# <trialsz> bits per sample (32-255).
+#
+#   REG <type> <port> <baud>
+#
+# Specify hardware device.  Supported types include "PEAR" only at
+# this time.  Port is serial port number (e.g. 1 for /dev/ttyS1);
+# <baud> is baud rate.
+#
+#   NETUP <script> <args> ...
+#   NETDOWN <script> <args> ...
+#
+# Provide script files to be run to bring up and tear down the network
+# connection on demand.  These will only be used if connection type is
+# DND.
+
+#EGG halley 1 10.0.0.111 halley PERM 1
+#BASKET halley 10.0.0.111
+#EGG halley 1 10.0.0.111 mercury DND 3
+#BASKET mercury 10.0.0.125
+
+#EGG noosphere 28 128.112.35.133 noosphere PERM 1
+#BASKET noosphere 128.112.35.133
+
+#   diesse -> diesse
+#EGG diesse 37 193.8.230.134 diesse PERM 1
+#BASKET diesse 193.8.230.134
+
+#   noosphere -> diesse
+EGG noosphere 28 128.112.35.133 diesse PERM 1
+BASKET diesse 193.8.230.134
+
+#   throop -> diesse
+#EGG throop 1003 193.8.230.132 diesse PERM 1 http://www.fourmilab.ch/
+#BASKET diesse 193.8.230.134
+
+#   jura -> jura
+#EGG jura 1004 193.8.230.130 jura PERM 1 http://www.fourmilab.ch/
+#BASKET jura 193.8.230.130
+
+#   diesse -> noosphere
+#EGG diesse.fourmilab.ch 37 193.8.230.134 noosphere PERM 1
+#BASKET noosphere 128.112.35.133
+
+#EGG diesse 37 193.8.230.134 jura PERM 1
+#BASKET jura 193.8.230.130
+
+#BASKET tonga 209.157.90.137
+#BASKET tonga1 209.157.90.138
+PROTOCOL 10 10 6 200
+#REG PEAR 2 9600
+REG PSEUDO 1 9600
+#REG ORION 1 9600
+NETUP pppscript up
+NETDOWN pppscript down
diff --git a/sample.pppscript b/sample.pppscript
new file mode 100755 (executable)
index 0000000..355a50f
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/csh
+
+if ("$1" == "up") then
+  echo "Bringing net up"
+
+  /usr/sbin/pppd connect '/usr/sbin/chat -v ABORT BUSY ABORT "NO CARRIER" "" ATZ OK ATS71=1 OK ATS80=1 OK ATDT4828060 CONNECT ""' /dev/cua0 115200 name halebopp remotename whitehole debug crtscts modem defaultroute 10.0.0.111:10.0.0.116
+
+  # Wait for connection success
+  set timer = 8
+  while ($timer > 0) 
+    if (-e "/var/run/ppp0.pid") exit 0
+    sleep 1
+    @ timer = $timer - 1
+  end
+
+  echo "Couldn't get it up!"
+  exit -1
+endif
+
+if ("$1" == "down") then
+  echo "Bringing net down"
+  if (! -e "/var/run/ppp0.pid") exit 0
+  set pid = `cat /var/run/ppp0.pid`
+  kill $pid
+
+  exit 0
+endif
+
+echo "Usage: pppscript up down"
+exit 1
diff --git a/storage.c b/storage.c
new file mode 100644 (file)
index 0000000..ac36436
--- /dev/null
+++ b/storage.c
@@ -0,0 +1,599 @@
+/* PROGRAM:    eggsh
+ * FILE:       $Header: /home/egg/src/RCS/storage.c,v 1.4 1998/12/31 22:07:56 ghn Exp $
+ * PURPOSE:    Data storage functions
+ * AUTHOR:     Greg Nelson
+ * DATE:       98-05-09
+ *
+ * REVISED:
+ * $Log: storage.c,v $
+ * Revision 1.4  1998/12/31 22:07:56  ghn
+ * Rev 5 code: includes multi-reg support, HTML, etc.
+ *
+ * Revision 1.3  1998/08/03 20:32:46  kelvin
+ * File byte-order independence, STORAGE_DEBUG
+ *
+ * Revision 1.2  1998/08/01  17:18:45  ghn
+ * Fixes to prevent core dumps and make "database" engine work correctly.
+ *
+ * Revision 1.1  1998/07/21 11:36:21  ghn
+ * Initial revision
+ *
+ * Copyright 1998 - Greg Nelson
+ * Redistributable under the terms of the GNU Public Licence (GPL)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include "global.h"
+#include "genlib.h"
+#include "storage.h"
+#include "errnos.h"
+
+/* Some systems (e.g. Linux) implement strdup but don't define
+   it by default in string.h.  Define it explicitly here, which
+   doesn't seem to do any harm on other systems.  If you encounter
+   an error on the following declaration, disable it for your
+   platform. */
+
+extern char *strdup (const char *s1);
+
+/* Eventually, these might deal with making a safe copy or mounting
+   and unmounting a partition. Until such time as we actually do
+   something with these, they are no-ops. */
+#define Protect(x)
+#define Unprotect(x)
+
+#define DATAFMT         "%Y%m/%%e"
+#define PROJSTART      901947600L
+
+static char *datafmt;                /* Data file format string */
+
+/*  Prototypes for forward functions.  */
+
+static int32 FileLoadPacket(FILE *fp, EggCarton *cart);
+static int32 next_filename(char *fn, uint32 tindex, int16 eggid,
+                          int16 *lastind, int16 mustexist);
+static int32 next_poss_fn(char *fn, uint32 tindex, int16 eggid);
+
+/* The seek optimisation table saves the file name, last
+   packet returned, and corresponding file of the next
+   packet (if any) in the file, for the last SEEK_OPT_MAX
+   requests.  This allows LoadNextPacket to avoid searching
+   the entire database for the next packet if the request
+   matches one in the seek optimisation table.
+
+*/
+
+#define SEEK_OPT_MAX MAX_BASKETS
+
+struct seekopt {
+    char filename[256];
+    uint32 last_time;
+    long next_packet;
+};
+
+static struct seekopt seekOpt[SEEK_OPT_MAX];
+static int seekOptIndex = 0;
+
+/* The initialization provides a way to specify a format for the
+   database file names.  If the argument is null, it uses a default
+   from the environment, or it takes the default given above.  This
+   string is used as an argument to strftime(3) which replaces certain
+   characters with corresponding fields of the date and time.  This
+   provides an automatic way of generating a logical new filename on a
+   periodic basis.  Note that you can call InitStorage as many times
+   as you wish to change the format for file names opened
+   subsequently. */
+
+int32 InitStorage(char *path) {
+  char *p, *np;
+  char ns[256], pa[12];
+  int changed = 0;
+
+  /* First, take this opportunity to clear the seek optimisation
+     table.  If we're changing the file name format, none of the
+     items in it will be valid in any case. */
+
+  memset(seekOpt, 0, sizeof seekOpt);
+
+  /* Set the path based on the environment variable, argument,
+     or default. */
+
+  if (path == NULL) {
+    if ((path = getenv("EGG_DATA")) == NULL) {
+      path = DATAFMT;
+    }
+  }
+
+  /* If the path contains any of our special "$" editing
+     codes, interpolate the requested text into the string.
+     Strftime "%" editing codes are left intact. */
+
+  ns[0] = 0;
+  p = path;
+  while ((np = strchr(p, '$')) != NULL) {
+    char *phrargs = np + 1;
+
+    /* Copy portion of string prior to phrase to output. */
+
+    if (np > p) {
+       int l = strlen(ns);
+
+       memcpy(ns + l, p, np - p);
+       ns[l + (np - p)] = 0;
+    }
+
+    /* Parse format phrase and possible arguments. */
+
+    while ((*phrargs != 0) && !isalpha(*phrargs)) {
+       phrargs++;
+    }
+    if (*phrargs == 0) {
+        fprintf(stderr, "Data format string error in:\n    %s\nunterminated $ phrase.\n", path);
+       exit(-1);
+    }
+
+    /* Copy arguments, if any, to second character of arguments
+       string for possible use by phrase interpreters in
+       sprintf. */
+
+    pa[0] = '%';                      /* Start editing phrase */
+    pa[1] = 0;
+    if (phrargs > (np + 1)) {
+       memcpy(pa + 1, np + 1, phrargs - (np + 1));
+       pa[1 + (phrargs - (np + 1))] = 0;
+    }
+/*fprintf(stderr, "Phrase arguments = <%s>\n", pa);*/
+
+    /* Now interpret the specific format phrase letters.
+       The value selected by each phrase should be concatenated
+       to the output string ns.  Available format phrases
+       are:
+
+          $b          Basket name
+          $e          Egg name
+          $[0][n]E    Egg number, right justified,
+                      optionally zero filled in n characters
+
+    */
+
+
+    switch (*phrargs) {
+
+        case 'b':                     /* Basket name */
+           strcat(ns, baskettable[0].name);
+           break;
+
+        case 'e':                     /* Egg name */
+           strcat(ns, eggtable[0].name);
+           break;
+
+        case 'E':                     /* Egg number, edited decimal number */
+            strcat(pa, "d");
+           sprintf(ns + strlen(ns), pa, eggtable[0].id);
+           break;
+
+       default:
+            fprintf(stderr, "Data format string error in:\n    %s\nunknown phrase $%c.\n",
+                   path, *phrargs);
+           exit(-1);
+    }
+
+    /* Adjust pointer to resume scan following the phrase
+       in the source string. */
+
+    p = phrargs + 1;
+    changed++;
+  }
+
+  if (changed) {
+    strcat(ns, p);                   /* Concatenate balance of string */
+    path = strdup(ns);
+    if (path == NULL) {
+        fprintf(stderr, "Cannot allocate memory for file name format string.\n");
+       exit(-1);
+    }
+/*printf("Expanded path name phrase = <%s>\n", path); exit(0);*/
+  }
+
+  datafmt = path;
+  return 0;
+}
+
+/* Save packet is the basic save function.  It does not take a
+   filename, relying on the datafmt variable created during
+   initialization to generate an appropriate filename. The
+   date for the saved data is based on the timestamp of the packet's
+   first record. */
+
+int32 SavePacket(EggCarton *cart) {
+  FILE         *fp;
+  char         *packet, datatmp[255], datafile[255], *sp;
+  int32        pktime, res;
+
+  pktime = cart->records[0].timestamp;
+#ifdef STORAGE_DEBUG
+  fprintf(stderr, "SavePacket for %lu: %s", pktime, asctime(gmtime((time_t *) &pktime)));
+#endif
+
+  /* Generate the file name corresponding to the date of
+     the first record in the packet.  Since we know the
+     date, there's no need to use next_filename. */
+
+  res = next_poss_fn(datafile, pktime, cart->hdr.eggid);
+
+  /* Open the file for appending. */
+
+  Unprotect(datafile);
+  fp = fopen(datafile, "a");
+
+  /* If we can't open the file, the odds are it's because
+     the file is in a directory we've yet to create. */
+
+  if (fp == NULL) {
+#ifdef STORAGE_DEBUG
+    fprintf(stderr, "SavePacket: no such file %s\n", datafile);
+#endif
+    sp = strrchr(datafile, '/');
+    while (sp) {
+      strncpy(datatmp, datafile, sp - datafile);
+      datatmp[sp - datafile] = 0;
+      mkdir(datatmp, 0777);
+#ifdef STORAGE_DEBUG
+      fprintf(stderr, "SavePacket: mkdir %s\n", datatmp);
+#endif
+      sp = strchr(sp + 1, '/');
+    }
+
+    /* Now try re-opening the file.  */
+
+    Unprotect(datafile);
+    if ((fp = fopen(datafile, "a")) == NULL) {
+      fprintf(stderr, "SavePacket: cannot create database file %s.\n", datafile);
+      exit(-1);
+    }
+  }
+  
+  if (!fp) {
+#ifdef STORAGE_DEBUG
+    fprintf(stderr, "SavePacket: error opening %s\n", datafile);
+#endif
+    return -2;
+  }
+  packet = Packetize(cart);
+  if (!packet) return -1;
+  fwrite(packet, sizeof(char), cart->hdr.pktsize, fp);
+  free(packet);
+  fclose(fp);
+  Protect(datafile);
+  cart->hdr.numrec = 0;
+
+  return 1;
+}
+
+/* Open database for reading, positioning to specified location.
+   If eggid is less than zero, it will load any packet. */
+int32 OpenDatabase(DBRec *dbp, uint32 tindex, int16 eggid) {
+  int32        res;
+
+#ifdef STORAGE_DEBUG
+  fprintf(stderr, "OpenDatabase: Egg = %d, tindex = %ld %s",
+    eggid, tindex, asctime(gmtime((time_t *) &tindex)));
+#endif
+  dbp->eggind = 0;
+  dbp->fp = NULL;
+  if ((res = next_filename(dbp->fn, tindex, eggid, &(dbp->eggind), TRUE)) < 0)
+    return res;
+    
+  Unprotect(dbp->fn);
+  dbp->fp = fopen(dbp->fn, "r");
+  if (dbp->fp == NULL) {
+    Protect(dbp->fn);
+#ifdef STORAGE_DEBUG
+  fprintf(stderr, "OpenDatabase -- EOF\n");
+#endif
+    return ERR_EOF;
+  } else {
+#ifdef STORAGE_DEBUG
+  fprintf(stderr, "OpenDatabase -- Opened %s\n", dbp->fn);
+#endif
+    return ERR_NONE;
+  }
+}
+
+/* Close database */
+int32 CloseDatabase(DBRec *dbp) {
+  fclose(dbp->fp);
+#ifdef STORAGE_DEBUG
+  fprintf(stderr, "CloseDatabase -- Closed %s\n", dbp->fn);
+#endif
+  Protect(dbp->fn);
+  dbp->fp = NULL;
+  /* We preserve file name; this lets us not open the same
+     file name again unless we want to. */
+  return ERR_NONE;
+}
+
+/* Reset database allows us to open same file name again. */
+int32 ResetDatabase(DBRec *dbp) {
+  *(dbp->fn) = 0;
+#ifdef STORAGE_DEBUG
+  fprintf(stderr, "ResetDatabase\n");
+#endif
+  return ERR_NONE;
+}
+
+/* Load next packet fitting specified time index and egg id.
+   If eggid is less than zero, it will load any packet. */
+
+static int32 LoadNextPacket(DBRec *dbp, uint32 tindex, int16 eggid, EggCarton *cart) {
+  EggCarton    pktbuf;
+  int32        res, i;
+  uint32       findex = tindex, now;
+
+  now = getzulutime(NULL);
+  if (findex < PROJSTART) {
+    findex = PROJSTART;
+  }
+#ifdef STORAGE_DEBUG
+  fprintf(stderr, "LoadNextPacket(%s, %lu, %d)\n", dbp->fn, tindex, eggid);
+#endif
+
+  /* See if the start address for this request is present
+     in the seek optimisation table.  If so, seek directly
+     to the address to avoid having to read through all
+     earlier packets in the database. */
+
+  i = seekOptIndex;
+  /* Conditioning while on tindex > 0 causes seek optimisation to be
+     skipped when a request for any packet is received. */
+  while (tindex > 0) { 
+      i--;
+      if (i < 0) {
+         i = SEEK_OPT_MAX - 1;
+      }
+      if (strcmp(seekOpt[i].filename, dbp->fn) == 0 &&
+         seekOpt[i].last_time == tindex) {
+         fseek(dbp->fp, seekOpt[i].next_packet, SEEK_SET);
+#ifdef STORAGE_DEBUG
+          fprintf(stderr, "LoadNextPacket; Seek optimised [%ld] to %ld for\n    file %s at %lu %s",
+             i,
+             seekOpt[i].next_packet, 
+             seekOpt[i].filename,
+             seekOpt[i].last_time,
+             asctime(gmtime((time_t *) &(seekOpt[i].last_time))));
+
+#endif
+         break;
+      }
+      if (i == seekOptIndex) {
+#ifdef STORAGE_DEBUG
+          fprintf(stderr, "LoadNextPacket; Cannot optimise seek in file %s\n    at %lu %s",
+             dbp->fn, tindex, asctime(gmtime((time_t *) &(tindex))));
+#endif
+         break;                      /* Search wrapped table--cannot optimise */
+      }
+  }
+  
+  while (1) {
+    res = FileLoadPacket(dbp->fp, &pktbuf);
+    if (res == ERR_EOF) {
+      /* File is finished.  Close and open the next.  We assume
+        that tindex and eggid have been tracking appropriately. */
+      CloseDatabase(dbp);
+
+      /* Advance findex to start of next day after the one we've
+        just exhausted. */
+
+#define SECONDS_PER_DAY (24L * 60 * 60)
+      findex = ((findex / SECONDS_PER_DAY) + 1) * SECONDS_PER_DAY;
+#ifdef STORAGE_DEBUG
+      fprintf(stderr, "LoadNextPacket; EOF, CloseDatabase, findex = %lu: %s",
+         findex, asctime(gmtime((time_t *) &findex)));
+#endif
+      if (findex > now) {
+#ifdef STORAGE_DEBUG
+          fprintf(stderr, "LoadNextPacket; EOF findex = %lu > now = %lu %s",
+         findex, now, asctime(gmtime((time_t *) &now)));
+#endif
+       return ERR_EOF;
+      }
+      /* Do not reset database; we won't open same one again. */
+      if ((res = OpenDatabase(dbp, findex, eggid)) < 0) return res;
+      continue;
+    }
+/*
+fprintf(stderr, "Eggid = %d Packet.eggid = %d\n", eggid, pktbuf.hdr.eggid);
+*/
+    if (eggid >= 0 && pktbuf.hdr.eggid != eggid) continue;
+    for (i = 0; i < pktbuf.hdr.numrec; i++) {
+/*fprintf(stderr, " Rec %ld timestamp = %lu  tindex = %lu\n", i, pktbuf.records[i].timestamp, tindex);
+*/
+      if (pktbuf.records[i].timestamp > tindex) {
+       memcpy(cart, &pktbuf, sizeof(EggCarton));
+
+       /* Save the location of the packet following this one
+          in the seek optimisation table. */
+
+       strcpy(seekOpt[seekOptIndex].filename, dbp->fn);
+       seekOpt[seekOptIndex].last_time = pktbuf.records[pktbuf.hdr.numrec - 1].timestamp;
+       seekOpt[seekOptIndex].next_packet = ftell(dbp->fp);
+#ifdef STORAGE_DEBUG
+        fprintf(stderr, "LoadNextPacket; Seek optimisation[%d] for %s\n    to address %ld for time %lu %s",
+           seekOptIndex,
+           seekOpt[seekOptIndex].filename,
+           seekOpt[seekOptIndex].next_packet, 
+           seekOpt[seekOptIndex].last_time,
+           asctime(gmtime((time_t *) &(seekOpt[seekOptIndex].last_time))));
+#endif
+       seekOptIndex = (seekOptIndex + 1) % SEEK_OPT_MAX;
+       return ERR_NONE;
+      }
+    }
+  }
+}
+
+/* Load next packet following a particular time index and egg id */
+
+int32 LoadPacket(uint32 tindex, int16 eggid, EggCarton *cart) {
+  DBRec db;
+  int32 res;
+
+  ResetDatabase(&db);  /* Here we need to be able to open same db as
+                          last time. */
+  if ((res = OpenDatabase(&db, tindex, eggid)) < 0) return res;
+  if ((res = LoadNextPacket(&db, tindex, eggid, cart)) < 0) return res;
+  return CloseDatabase(&db);
+}
+
+static int32 FileLoadPacket(FILE *fp, EggCarton *cart) {
+  char *rbuf;
+  int32 res;
+  uint16 pksize;
+
+  if (feof(fp)) return ERR_EOF;
+
+  if (fread(&(cart->hdr.type), sizeof(uint16), 1, fp) != 1) {
+    if (feof(fp)) return ERR_EOF; else return ERR_CNREAD;
+  }
+  if (fread(&(cart->hdr.pktsize), sizeof(uint16), 1, fp) != 1) {
+    if (feof(fp)) return ERR_EOF; else return ERR_CNREAD;
+  }
+
+  /* Since the file packet is in network byte order, we need to
+     be sure the length field is in host order before using
+     it to allocate and read the balance of the packet from the
+     file.  Once that's done, Unpacketize will take responsibility
+     for getting all the packet fields into host order. */
+  pksize = ntohs(cart->hdr.pktsize);
+
+  rbuf = (char *)malloc(pksize);
+  if (!rbuf) return ERR_NOMEM;
+
+  memcpy(rbuf, &(cart->hdr), 2*sizeof(uint16));
+  if (fread(rbuf+4, pksize - 4, 1, fp) != 1) return ERR_CNREAD;
+  
+  res = Unpacketize(cart, rbuf);
+  free(rbuf);
+
+  return res;
+}
+
+static int32 next_filename(char *fn, uint32 tindex, int16 eggid,
+                   int16 *lastind, int16 mustexist) {
+  struct stat  statbuf;
+  uint32       findex, now;
+  int32        res;
+  char         ldf[255];
+
+#ifdef STORAGE_DEBUG
+  fprintf(stderr, "next_filename: egg = %d, mustexist = %d, tindex = %lu %s",
+    eggid, mustexist, tindex, asctime(gmtime((time_t *) &tindex)));
+#endif
+  if (eggid < 0 && !mustexist) {
+#ifdef STORAGE_DEBUG
+    fprintf(stderr, "next_filename: egg ID out of range.\n");
+#endif
+    return ERR_INRANGE;
+  }
+
+  /* Can't go back before project began. */
+  now = getzulutime(NULL);
+  if (tindex < PROJSTART) {
+    findex = PROJSTART;
+  } else {
+    findex = tindex;
+  }
+
+  /* Search possible file names until one is found.  We jump by days,
+     assuming files don't have a greater granularity than that.  In
+     the existence-testing process, we are checking to make sure the
+     file name is different each time. */
+
+  if (lastind == NULL) eggid = eggtable[0].id;
+  else {
+    if (eggid < 0) eggid = eggtable[(*lastind)++].id;
+    if (*lastind == numeggs) *lastind = 0;
+  }
+
+  strcpy(ldf, fn);
+  do {
+    res = next_poss_fn(fn, findex, eggid);
+    findex += 86400L;
+    if (!strcmp(fn, ldf)) continue;
+    strcpy(ldf, fn);
+    /* Check for file.  If stat fails, reason is ENOENT, isn't it? */
+    if (stat(fn, &statbuf) >= 0) {
+#ifdef STORAGE_DEBUG
+      fprintf(stderr, "next_filename: Found file %s\n", fn);
+#endif
+       return ERR_NONE;
+    }
+    if (!mustexist) {
+#ifdef STORAGE_DEBUG
+        fprintf(stderr, "next_filename: File %s does not exist.\n", fn);
+#endif
+       return ERR_NOENT;
+    }
+  } while (findex < now+86400L);
+
+#ifdef STORAGE_DEBUG
+      fprintf(stderr, "next_filename: End of file.\n");
+#endif
+  return ERR_EOF;
+}
+
+/* tindex and eggid must be defined here */
+static int32 next_poss_fn(char *fn, uint32 tindex, int16 eggid) {
+  char         datatmp[255], *sp;
+  int16        eggind;
+  struct tm    *tm;
+
+  tm = gmtime((time_t *) &tindex);
+  strftime(datatmp, 255, datafmt, tm);
+#ifdef STORAGE_DEBUG
+  fprintf(stderr, "next_poss_fn: egg = %d, tindex = %lu %s",
+    eggid, tindex, asctime(tm));
+#endif
+
+  /* If an %e phrase remains after processing by strftime
+     (it should be specified as %%e in the original
+     DATAFMT specification), interpolate the egg number
+     from this packet. */
+
+  if ((sp = strchr(datatmp, '%')) != NULL) {
+    if (sp[1] == 'e') {
+      for (eggind = 0;
+          eggind < numeggs && eggtable[eggind].id != eggid;
+          eggind++);
+      if (eggind == numeggs) {
+#ifdef STORAGE_DEBUG
+        fprintf(stderr, "next_poss_fn: Egg number out of range.\n");
+#endif
+       return ERR_INRANGE;
+      }
+      *sp = 0;
+      strcpy(fn, datatmp);
+      strcat(fn, eggtable[eggind].name);
+      strcat(fn, sp + 2);
+    } else {
+      fprintf(stderr, "Bad file format expr: %%%c\n", sp[1]);
+      strcpy(fn, datatmp);
+      return ERR_INRANGE;
+    }
+  } else {
+    strcpy(fn, datatmp);
+  }
+#ifdef STORAGE_DEBUG
+  fprintf(stderr, "next_poss_fn: File name = %s\n", fn);
+#endif
+  return ERR_NONE;
+}
diff --git a/storage.h b/storage.h
new file mode 100644 (file)
index 0000000..fc88960
--- /dev/null
+++ b/storage.h
@@ -0,0 +1,63 @@
+/* PROGRAM:     eggsh
+ * FILE:        $Header: /home/egg/src/RCS/storage.h,v 1.3 1998/12/31 22:11:05 ghn Exp $
+ * PURPOSE:     Definitions for data storage functions
+ * AUTHOR:      Greg Nelson
+ * DATE:        98-05-09
+ *
+ * REVISED:
+ * $Log: storage.h,v $
+ * Revision 1.3  1998/12/31 22:11:05  ghn
+ *  Rev 5 code: includes multi-reg support, HTML, etc.
+ *
+ * Revision 1.2  1998/08/01 17:19:48  ghn
+ * Added ResetDatabase function.
+ *
+ * Revision 1.1  1998/07/21 11:35:10  ghn
+ * Initial revision
+ *
+ * Copyright 1998 - Greg Nelson
+ * Redistributable under the terms of the GNU Public Licence (GPL)
+ */
+
+typedef struct dbase {
+  FILE *fp;
+  int16 eggind;
+  char fn[256];
+} DBRec;
+
+/* The initialization provides a way to specify a format for the
+   database file names.  If the argument is null, it uses a default
+   from the environment, or a compiled default.  This string is used
+   as an argument to strftime(3) which replaces certain characters
+   with corresponding fields of the date and time.  This provides an
+   automatic way of generating a logical new filename on a periodic
+   basis.
+
+   The other function of the initialization is to clear static
+   variable used to incrementally collect records for SaveRecord until
+   a complete packet is available for storage to disk. */ 
+extern int32 InitStorage(char *path);
+
+/* Save packet is the basic save function.  It does not take a
+   filename, relying on the datafmt variable created during
+   initialization to generate an appropriate filename.  The
+   date for the saved data is based on the timestamp of the packet's
+   first record. */
+extern int32 SavePacket(EggCarton *cart);
+
+/* Open database for reading, positioning to specified location.
+   If eggid is less than zero, it will load any packet. */
+extern int32 OpenDatabase(DBRec *db, uint32 tindex, int16 eggid);
+
+/* Close database */
+extern int32 CloseDatabase(DBRec *db);
+
+/* Reset database allows us to open same file name again. */
+extern int32 ResetDatabase(DBRec *dbp);
+
+/* Load next packet following a particular time index and egg id.  If
+   eggid is less than zero, it will load any packet.  This is
+   essentially a shortcut for getting one packet. */
+
+extern int32 LoadPacket(uint32 tindex, int16 eggid, EggCarton *cart);
+
diff --git a/testmain.c b/testmain.c
new file mode 100644 (file)
index 0000000..d8cd3a8
--- /dev/null
@@ -0,0 +1,220 @@
+/* PROGRAM:     testmain
+ * FILE:        $Header: /home/egg/src/RCS/testmain.c,v 1.1 1998/07/21 11:34:11 ghn Exp $
+ * PURPOSE:     First draft eggsh
+ * AUTHOR:      Greg Nelson
+ * DATE:        98-05-09
+ *
+ * REVISED:     $Log: testmain.c,v $
+ * REVISED:     Revision 1.1  1998/07/21 11:34:11  ghn
+ * REVISED:     Initial revision
+ * REVISED:
+ * Copyright 1998 - Greg Nelson
+ * Redistributable under the terms of the GNU Public Licence (GPL)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "global.h"
+#include "genlib.h"
+#include "hwapi.h"
+#include "collect.h"
+#include "storage.h"
+#include "network.h"
+#include "errnos.h"
+
+#define BASKETHOST     "halebopp.qtmsys.com"
+
+char *baskethost = BASKETHOST;
+
+void MakeAwake(AwakePacket *pkt);
+void MakeDataPkt(char **pkt, EggCarton *src);
+
+int main(int argc, char *argv[]) {
+  int                  res, cdres, i;
+  int32 sdlisten;
+  double sps;
+  int32 collyes, collno, alignover, alignwait;
+  DevOpts devopts;
+  CollectRecord                coll;
+  EggCarton            retrcart;
+  AwakePacket          awake;
+  ReqPacket            *rpktp;
+  struct sockaddr_in   rhost, bhost;
+  char                 *pktbuf, *outpktbuf;
+  uint16               pkttype, pktsize;
+
+  pgmname = argv[0];
+
+  if (argc != 6) {
+    printf("Usage: %s <ttynum> <baud> <trialsz> <sec_rec> <samp_rec>\n", pgmname);
+    exit(-1);
+  }
+
+  devopts.port = atoi(argv[1]);
+  devopts.baud = atoi(argv[2]);
+  coll.opts.trialsz = atoi(argv[3]);
+  coll.opts.sec_rec = atoi(argv[4]);
+  coll.opts.samp_rec = atoi(argv[5]);
+  coll.opts.eggid = GetID();
+
+  coll.opts.rec_pkt = SEC_PKT / coll.opts.sec_rec;
+  if (coll.opts.rec_pkt < 1) coll.opts.rec_pkt = 1;
+  if (coll.opts.rec_pkt > MAXREC_PKT) coll.opts.rec_pkt = MAXREC_PKT;
+
+  if (coll.opts.trialsz < MINBITS || coll.opts.trialsz > MAXBITS) {
+    fprintf(stderr, "Trial size must be between %d and %d.\n",
+           MINBITS, MAXBITS);
+    exit(-1);
+  }
+
+  if (coll.opts.sec_rec < 1 || coll.opts.sec_rec > MAXSEC_REC) {
+    fprintf(stderr, "Seconds/record must be between 1 and %d.\n", MAXSEC_REC);
+    exit(-1);
+  }
+
+  if (coll.opts.samp_rec < 1 || coll.opts.samp_rec > MAXSAMP_REC) {
+    fprintf(stderr, "Samples/record must be between 1 and %d.\n", MAXSAMP_REC);
+    exit(-1);
+  }
+
+  sps = (double)coll.opts.samp_rec / (double)coll.opts.sec_rec;
+
+  printf("Effective sample rate is about %f samp/sec or %f bits/sec\n",
+        sps, coll.opts.trialsz * sps);
+  printf("Packets contain %d records\n", coll.opts.rec_pkt);
+
+  if ((coll.dd = OpenDev(&devopts)) < 0) exit(1);
+  if (EvalSpeed(coll.dd) < coll.opts.trialsz * sps) {
+    fprintf(stderr, "Requested speed exceeds device capabilities.\n");
+    exit(-1);
+  }
+
+  coll.sampct = 0;
+  collyes = collno = 0;
+  alignover = alignwait = 0;
+
+  InitStorage(NULL);
+  sdlisten = InitNetwork(EGGPORT);
+
+  /* Get us aligned for the first time */
+  while(AlignRecord(&coll, TRUE) < 0);
+
+  while(1) {
+    res = NetListen(sdlisten, &pktbuf, &rhost, FALSE);
+    if (res < 0 && res != ERR_COMM_TMOUT) {
+      fprintf(stderr, "NetListen error: %d\n", res);
+      continue;
+    }
+    if (res == ERR_NONE) {
+      pkttype = ((GenPacket *)pktbuf)->type;
+      pktsize = ((GenPacket *)pktbuf)->pktsize;
+      switch(pkttype) {
+      case DATA_PACKET:
+       fprintf(stderr, "Egg received data?\n");
+       break;
+      case REQ_PACKET:
+       /* Basket wants data! */
+       rpktp = (ReqPacket *)pktbuf;
+       
+       /* Find the desired data. */
+       res = LoadNextPacket(rpktp->starttm, &retrcart);
+
+       if (res == ERR_NONE) {
+         /* NetPacketize it */
+         MakeDataPkt(&outpktbuf, &retrcart);
+         rhost.sin_port = htons(BASKETPORT);
+         res = NetTalk(&rhost, outpktbuf);
+         if (res < 0) {
+           fprintf(stderr, "NetTalk failed (%d)\n", res);
+         }
+         free(outpktbuf);
+       }
+       break;
+      case AWAKE_PACKET:
+       fprintf(stderr, "Egg received awake?\n");
+       break;
+      case SETTINGS_PACKET:
+       /* Install new settings! */
+       break;
+      }
+      free(pktbuf);
+    }
+    
+    if (coll.sampct == 0) {
+      cdres = AlignRecord(&coll, FALSE);
+      if (cdres < 0) {
+       if (cdres == -3) alignover++; else alignwait++;
+       continue;
+      }
+    }
+    cdres = CollectData(&coll);
+    if (cdres < 0) {
+      collno++;
+      continue;
+    }
+    collyes++;
+    if (cdres == 1) {
+      SaveRecord(&coll);
+      MakeAwake(&awake);
+      i = NetGetAddr(&bhost, baskethost, BASKETPORT);
+      i = NetTalk(&bhost, (char *)&awake);
+      printf(" collect:nodata over:wait = %ld:%ld %ld:%ld\n",
+            collyes, collno, alignover, alignwait);
+      printf("Record: %d points (transmission %d)\n  %ld: ",
+            coll.sampct, i, coll.data.timestamp);
+      for (i = 0; i < coll.sampct; i++)
+       printf("%3d ", coll.data.trials[i]);
+      printf("\n");
+      coll.sampct = 0;
+    }
+  }
+
+  return 0;
+}
+
+
+void MakeAwake(AwakePacket *pkt) {
+  struct timeval now;
+
+  pkt->type = AWAKE_PACKET;
+  pkt->pktsize = sizeof(AwakePacket);
+  pkt->eggid = GetID();
+  getzulutime(&now);
+  pkt->nowtm = now.tv_sec;
+}
+
+void MakeDataPkt(char **pkt, EggCarton *src) {
+  uint16 pktsize, offset, rec, sbuf;
+  uint32 lbuf;
+
+  pktsize = sizeof(EggHeader);
+  pktsize += src->hdr.numrec * (sizeof(uint32) + 
+                               src->hdr.samp_rec * sizeof(trial));
+  pktsize += sizeof(uint16);   /* Checksum */
+
+  src->hdr.type = DATA_PACKET;
+  src->hdr.pktsize = pktsize;
+
+  *pkt = (char *)malloc(pktsize);
+  memcpy(*pkt, &(src->hdr), sizeof(EggHeader));
+  offset = sizeof(EggHeader);
+
+  for (rec = 0; rec < src->hdr.numrec; rec++) {
+    lbuf = src->records[rec].timestamp;
+    memcpy((*pkt)+offset, &lbuf, sizeof(uint32));
+    offset += sizeof(uint32);
+    /* Assumes sizeof(trial) = 1 */
+    memcpy((*pkt)+offset, &(src->records[rec].trials), src->hdr.samp_rec);
+    offset += src->hdr.samp_rec;
+  }
+
+  /* Normal CRC */
+  sbuf = BlockCRC16(*pkt, offset);
+  memcpy((*pkt)+offset, &sbuf, sizeof(uint16));
+  offset += sizeof(uint16);
+
+  if (offset != pktsize) {
+    fprintf(stderr, "Error in packet generation!\n");
+  }
+}
+
diff --git a/testudp.c b/testudp.c
new file mode 100644 (file)
index 0000000..d13b01e
--- /dev/null
+++ b/testudp.c
@@ -0,0 +1,89 @@
+/* PROGRAM:     testudp
+ * FILE:        $Header: /home/egg/src/RCS/testudp.c,v 1.2 1998/07/21 11:34:48 ghn Exp $
+ * PURPOSE:     Testing UDP communications (fake basket)
+ * AUTHOR:      Greg Nelson
+ * DATE:        98-05-09
+ *
+ * REVISED:     $Log: testudp.c,v $
+ * REVISED:     Revision 1.2  1998/07/21 11:34:48  ghn
+ * REVISED:     Testing UDP communications (fake basket)
+ * REVISED:
+ * Copyright 1998 - Greg Nelson
+ * Redistributable under the terms of the GNU Public Licence (GPL)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include "global.h"
+#include "genlib.h"
+#include "hwapi.h"
+#include "collect.h"
+#include "storage.h"
+#include "network.h"
+
+int main(int argc, char *argv[]) {
+  ReqHeader            rhdr;
+  struct protoent      *pp;
+  struct hostent       *hp;
+  struct sockaddr_in   rhost;
+  int32                        i;
+  int32                        out_sock;
+
+  pgmname = argv[0];
+
+  if (argc != 2) {
+    printf("Usage: %s <host>\n", pgmname);
+    exit(-1);
+  }
+
+  if ((pp = getprotobyname("udp")) == NULL) {
+    perror("getprotobyname");
+    exit(-1);
+  }
+
+  if ((hp = gethostbyname(argv[1])) == NULL) {
+    fprintf(stderr, "gethostbyname(%s): %s", argv[1], strerror(errno));
+    exit(-1);
+  }
+
+  if (hp->h_addrtype != AF_INET) {
+    fprintf(stderr, "Host is not on the internet!\n");
+    exit(-1);
+  }
+
+  rhost.sin_port = htons(EGGPORT);
+  rhost.sin_family = AF_INET;
+  memcpy(&(rhost.sin_addr), hp->h_addr, hp->h_length);
+
+  rhdr.type = REQ_PACKET;
+  rhdr.pktsize = sizeof(ReqHeader);
+  rhdr.eggid = 30800;  /* halebopp! */
+  rhdr.samp_rec = 10;
+  rhdr.sec_rec = 10;
+  rhdr.rec_pkt = 6;
+  rhdr.trialsz = 200;
+  rhdr.starttm = 0;
+  rhdr.cksum = BlockCRC16((byte *)&rhdr, sizeof(ReqHeader)-2);
+
+  if ((out_sock = socket(AF_INET, SOCK_DGRAM, pp->p_proto)) < 0) {
+    perror("socket");
+    exit(-1);
+  }
+       
+  i = sendto(out_sock, &rhdr, rhdr.pktsize, 
+            0, (struct sockaddr *)&rhost,
+            sizeof(struct sockaddr));
+  if (i == -1) {
+    perror("sendto");
+    exit(-1);
+  }
+  if (i != rhdr.pktsize) {
+    fprintf(stderr, "Failed to send complete packet (%ld/%d)\n",
+           i, rhdr.pktsize);
+    exit(-1);
+  }
+  
+  exit(0);
+}
diff --git a/usleep.c b/usleep.c
new file mode 100644 (file)
index 0000000..1895616
--- /dev/null
+++ b/usleep.c
@@ -0,0 +1,77 @@
+/*
+
+    Emulation of BSD usleep()
+
+    Note that this is not a complete emulation in that
+    it destroys any pre-existing setitimer(), but it's
+    good enough for programs which don't have interval
+    timers running across calls on usleep().  It does
+    save and restore an existing SIGALRM handler.
+
+*/
+
+#ifdef USLEEP
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+
+#ifdef Solaris
+typedef void (*signalFUNC)(int);
+#define signal(a, b)   sigset(a, (signalFUNC) b)
+#define signalFUNCreturn (signalFUNC)
+#endif
+
+#ifndef signalFUNCreturn
+#define signalFUNCreturn
+#endif
+
+#ifdef FLUSH
+#include <termios.h>
+int flush_fd = -1;                   /* File descriptor for auto-flush */
+#endif
+
+volatile static int waiting;
+
+static void getalrm(int i)
+{
+    waiting = 0;
+}
+
+void sf_usleep(unsigned t)
+{
+    static struct itimerval it;
+    void (*oldsig)();
+
+    oldsig = signalFUNCreturn signal(SIGALRM, getalrm);
+#ifdef FLUSH
+#define FLUSHTIME 100000             /* Flush interval in microseconds */
+    if (flush_fd != -1) {
+       while (t > FLUSHTIME) {
+           it.it_value.tv_sec = 0;
+           it.it_value.tv_usec = FLUSHTIME;
+           t -= FLUSHTIME;
+           tcflush(flush_fd, TCIFLUSH);
+           waiting = 1;
+           if (setitimer(ITIMER_REAL, &it, NULL))
+               return /*error*/;
+           while (waiting) {
+               pause();
+           }
+           (void) signal(SIGALRM, getalrm);
+       }
+    }
+#endif
+
+    it.it_value.tv_sec = t / 1000000;
+    it.it_value.tv_usec = t % 1000000;
+    waiting = 1;    
+    if (setitimer(ITIMER_REAL, &it, NULL))
+       return /*error*/;
+    while (waiting) {
+       pause();
+    }
+    signal(SIGALRM, oldsig);
+}
+#endif
diff --git a/version.h b/version.h
new file mode 100644 (file)
index 0000000..636b0f1
--- /dev/null
+++ b/version.h
@@ -0,0 +1,7 @@
+/*
+
+        Egg and basket version information.
+
+*/
+
+#define Version     "Release 5.1 (1999-02-28 Dynamic Eggs)"
diff --git a/xdsub.c b/xdsub.c
new file mode 100644 (file)
index 0000000..3bcd022
--- /dev/null
+++ b/xdsub.c
@@ -0,0 +1,107 @@
+/*
+
+                          Hex dump utility
+
+                           by John Walker
+              WWW home page: http://www.fourmilab.ch/
+
+               This program is in the public domain.
+
+*/
+
+#ifdef HEXDUMP
+
+#include <stdio.h>
+#include <string.h>
+
+#define EOS     '\0'
+
+static char addrformat[80] = "%6X";
+static char dataformat1[80] = "%02X";
+static int bytesperline = 16, doublechar = 0,
+          dflen = 2;
+static unsigned long fileaddr;
+static unsigned char lineecho[32];
+
+/*  OUTLINE  --  Edit a line of binary data into the selected output
+                format.  */
+
+static void outline(FILE *out, unsigned char *dat, int len)
+{
+    char oline[132];
+    int i;
+
+    sprintf(oline, addrformat, fileaddr);
+    strcat(oline, ":");
+    for (i = 0; i < len; i++) {
+       char outedit[80];
+
+       sprintf(outedit, dataformat1, dat[i]);
+        strcat(oline, (i == (bytesperline / 2)) ? "  " : " ");
+       strcat(oline, outedit);
+    }
+
+    if (doublechar) {
+       char oc[2];
+       int shortfall = ((bytesperline - len) * (dflen + 1)) +
+                       (len <= (bytesperline / 2) ? 1 : 0);
+
+       while (shortfall-- > 0) {
+            strcat(oline, " ");
+       }
+       oc[1] = EOS;
+        strcat(oline, " | ");
+       for (i = 0; i < len; i++) {
+           int b = dat[i];
+
+            /* Map non-printing characters to "." according to the
+              definitions for ISO 8859/1 Latin-1. */
+
+            if (b < ' ' || (b > '~' && b < 145)
+                       || (b > 146 && b < 160)) {
+                b = '.';
+           }
+           oc[0] = b;
+           strcat(oline, oc);
+       }
+    }
+    strcat(oline, "\n");
+    fputs(oline, out);
+}
+
+/*  XD --  Dump a buffer.
+
+           xd(out, buf, bufl, dochar);
+
+           out     FILE * to which output is sent.
+           buf     Address of buffer to dump.
+           bufl    Buffer length in bytes.
+           dochar  If nonzero, show ASCII/ISO characters
+                   as well as hexadecimal.
+
+*/
+
+void xd(FILE *out, void *bub, int bufl, int dochar)
+{
+    int b, bp;
+    unsigned char *buf = (unsigned char *) bub;
+
+    bp = 0;
+    fileaddr = 0;
+    doublechar = dochar;
+
+    while (bufl-- > 0) {
+       b = *buf++;
+       if (bp >= bytesperline) {
+           outline(out, lineecho, bp);
+           bp = 0;
+           fileaddr += bytesperline;
+       }
+       lineecho[bp++] = b;
+    }
+
+    if (bp > 0) {
+       outline(out, lineecho, bp);
+    }
+}
+#endif