2 * FILE: $Header: /home/egg/src/RCS/egg.c,v 1.8 1999/02/28 20:01:43 ghn Exp $
3 * PURPOSE: EGG site data collection
9 * Revision 1.8 1999/02/28 20:01:43 ghn
10 * Version 5.1: Added command line parsing for interface/port configuration.
11 * Changed default interface to 0.0.0.0, so that egg will normally bind to
12 * all incoming interfaces. Reports interface/port it is using, right before
13 * the screen gets cleared and you can't see it.
15 * Revision 1.7 1999/01/01 23:57:20 ghn
16 * Remove excess code associated with CPU-bound version and "OLDWAY" UI
17 * and egg HW selection. Add back in updated block algorithm description.
18 * Allow non-CPU-bound version to exchange more than 1 packet per second.
19 * Get rid of warnings when network is not reachable, assuming this is a
22 * Revision 1.6 1998/12/31 22:07:56 ghn
23 * Rev 5 code: includes multi-reg support, HTML, etc.
25 * Revision 1.5 1998/08/03 20:35:55 kelvin
28 * Revision 1.4 1998/08/01 21:34:26 ghn
29 * Connected user interface and added PSEUDO suppor into main line.
31 * Revision 1.3 1998/08/01 18:51:25 ghn
32 * Added John's byte-order-independence changes.
34 * Revision 1.2 1998/08/01 17:04:26 ghn
35 * Lots of fixes from John, plus DND support.
37 * Revision 1.1 1998/07/21 11:41:35 ghn
40 * Copyright 1998 - Greg Nelson
48 #include <sys/types.h>
49 #include <sys/socket.h>
51 #include <netinet/in.h>
64 /* Good old Linux doesn't define the following functions
65 by default in the #include files the manual page claims
66 it does, and the conditional declarations with __USE_xx
67 doesn't work. Explicitly declare them, which doesn't
68 seem to do any harm on systems where these functions are
69 properly declared. If you get errors on the following
70 declarations, you can probably get around them by #ifdef-ing
71 the declarations off for your platform. */
73 extern int nice(int inc);
74 extern int strcasecmp(const char *s1, const char *s2);
76 /* Prototypes for forward functions. */
78 static void MakeAwake(AwakePacket *pkt);
79 static void MakeDataPkt(char **pkt, EggCarton *src);
80 static void LoadRCFile(char *filename);
81 static double SetCollOpts(void);
82 static int GreetBasket(void);
83 static void handle_sigkill(int arg), handle_sighup(int arg);
85 EggEntry eggtable[MAX_EGGS];
86 BasketEntry baskettable[MAX_BASKETS];
88 REG_driver *configuredREG = NULL; /* Configured REG driver */
90 short numeggs = 0, numbaskets = 0;
92 static DevOpts devopts; /* Device options for REG */
93 static CollectRecord coll;
94 static EggCarton savebuffer;
95 char *pgmname; /* Program name from argv[0] */
96 char *myaddr; /* Interface to bind */
97 int16 myport; /* Service port to bind */
98 uint32 lastDataSent = 0; /* Time last packet sent to basket */
100 /* If no priority increment has been specified at compile time, set to
101 our default of ±10. [+/- 10, for folks without 8bit editors] */
106 static int niceness = NICE; /* Priority increment/decrement for collection */
108 /* Status exported to user interface. */
110 uint32 time_latency = 0, time_housekeeping = 0;
113 static double mean_Packet = 0.0, mean_Grand = 0.0;
114 static int32 total_Packets = 0;
117 /* resetCarton -- Clear existing data from savebuffer and
118 reinitialise for a sampling interval
119 beginning at the specified start_time. */
121 static void resetCarton(uint32 start_time)
126 /* Save current collection options to packet. */
128 memcpy(&(savebuffer.hdr), &(coll.opts), sizeof(EggHeader));
130 /* Set number of records in packet and clear record
131 buffers to missing data. */
133 savebuffer.hdr.numrec = savebuffer.hdr.rec_pkt; /* Mark all records present */
135 /* Align start_time to even multiple of seconds per
138 sec_pkt = savebuffer.hdr.sec_rec * savebuffer.hdr.rec_pkt;
139 start_time = sec_pkt * (start_time / sec_pkt);
141 for (i = 0; i < savebuffer.hdr.rec_pkt; i++) {
142 savebuffer.records[i].timestamp = start_time;
143 start_time += savebuffer.hdr.sec_rec;
145 memset(savebuffer.records[i].trials, EGG_MISSING_DATA, MAXSAMP_REC);
147 #error "Can't represent MAXBITS in a byte value."
152 /* saveProtocol -- Save current protocol in the .eggprotocolrc file. */
154 static void saveProtocol(void)
158 rcfile = fopen(".eggprotocolrc", "w");
159 if (rcfile == NULL) {
160 fprintf(stderr, "%s: Cannot create .eggprotocolrc file.\n", pgmname);
163 fprintf(rcfile, "#\n# Current protocol for egg\n#\n");
164 fprintf(rcfile, "# PROTOCOL <samp_rec> <sec_rec> <rec_pkt> <trialsz>\n");
165 fprintf(rcfile, "PROTOCOL %d %d %d %d\n", protocol.samp_rec, protocol.sec_rec,
166 protocol.rec_pkt, protocol.trialsz);
170 static void Usage(void) {
171 printf("Usage: %s [-i interface] [-p port]\n", pgmname);
177 int main(int argc, char *argv[]) {
178 char *pktbuf, *outpktbuf;
179 uint16 pkttype, pktsize;
181 struct sockaddr_in rhost;
185 int32 lastconn, lastsend;
187 #ifdef SIGACTION_WORKING
188 struct sigaction sigact;
190 FILE *pidfile, *rcfile;
195 /* Defaults correspond to original usage */
200 if (argv[0][0] == '-') {
202 case 'i': /* Specify interface */
203 if (argc < 2) Usage();
207 case 'p': /* Specify port */
208 if (argc < 2) Usage();
209 myport = atoi(argv[1]);
210 if (myport < 0) Usage();
223 /* Solaris has an eccentric definition of sigaction() which
224 doesn't seem to even work according to Sun's own
225 documentation. (sa_flags is a 4 element array of
226 unsigned longs, with no mention of how one stores the
227 flags into it.) Let's just use plain old signal for
229 signal(SIGKILL, handle_sigkill);
230 signal(SIGHUP, handle_sighup);
232 #ifdef SIGACTION_WORKING
233 sigact.sa_handler = handle_sigkill;
236 sigact.sa_restorer = NULL;
237 sigaction(SIGKILL, &sigact, NULL);
239 sigact.sa_handler = handle_sighup;
240 sigaction(SIGHUP, &sigact, NULL);
242 signal(SIGKILL, handle_sigkill);
243 signal(SIGHUP, handle_sighup);
247 fprintf(stderr, "Starting egg %s.\n", Version);
249 /* Save our process ID in a file for folks who might wish
250 to send us a signal. */
252 pidfile = fopen("eggsample.pid", "w");
253 if (pidfile == NULL) {
254 fprintf(stderr, "Unable to create eggsample.pid file.\n");
257 fprintf(pidfile, "%d\n", getpid());
260 /* If we aren't being run by the super-user, disable the
261 priority raising and lowering mechanism. */
268 printf("User ID = %d. Niceness = %d.\n", getuid(), niceness);
269 printf("REG drivers installed: ");
270 for (i = 0; reg_table[i] != NULL; i++) {
271 printf(" %s", reg_table[i]->reg_name);
278 /* If an auxiliary configuration file exists, load it on
279 top of the arguments loaded from the main RC file. If
280 it doesn't exist, create it. */
282 if ((rcfile = fopen(".eggprotocolrc", "r")) != NULL) {
284 LoadRCFile(".eggprotocolrc");
289 if (configuredREG == NULL) {
290 fprintf(stderr, "%s: RC error, no REG specified for egg.\n",
296 printf("REG configured: %s = %d, %d, %d\n", configuredREG ->reg_name,
297 devopts.type, devopts.port, devopts.baud);
304 if ((coll.dd = OpenDev(&devopts)) < 0) {
305 fprintf(stderr, "Couldn't talk to hardware device.\n");
309 if (EvalSpeed(coll.dd) < coll.opts.trialsz * sps) {
310 fprintf(stderr, "Requested speed exceeds device capabilities.\n");
316 fprintf(stderr, "TCP configured: %s.%d\n", myaddr, myport);
318 fprintf(stderr, "TCP configured: hostname.%d\n", myport);
324 sdlisten = -1; /* No conn yet. */
325 lastconn = lastsend = 0;
327 /* Initialise carton to zero time, indicating no data in
331 /* Initialize storage system */
332 InitStorage("%Y-%m/%Y-%m-%d-$06E");
334 /* Inner loop is currently two threads which are both processed
335 together through NBIO.
337 2A. Adjust priority and sleep until time computed below (2H).
338 2B. Busy wait until second ticks over.
339 2C. Empty REG input buffer (if needed), then collect sample.
340 2D. Lower priority to handle bookkeeping; save packet.
341 2E. Update user interface.
343 2F. Send awake packet if connival is passed.
344 2G. [DND only] Put net to sleep if we've been waiting more than a minute
346 1A. Wait for connection. When a connection comes in,
347 packet has been decrypted and verified.
348 1B. Validate connection.
349 1C. Reply to connection.
350 1D. If we have more than MINSLEEP time left in second,
353 2H. Compute new sleep time.
357 static int sleeptime = 0;
358 static struct timeval t, lt = {0, 0};
368 #define SLACK 50000 /* CPU loop resynchronisation time in microseconds */
372 #define MINSLEEP 100000 /* Minimum sleep time, usec */
375 /* 2A. If we have successfully computed a sleep time until
376 the window opens to collect the next sample, give up
377 the CPU until that time arrives. */
380 nice(-niceness); /* Raise priority for re-dispatch */
383 Usleep((unsigned) sleeptime);
386 /* 2B. We're now close to the start of the next second. Watch the
387 time until the second changes, then collect the next sample.
388 Note that this method of doing things enforces the one
389 sample/second that has become standard practise. The code at
390 the bottom of the loop could be changed to allow for a longer
391 interval, but this code enforces a minimum of a 1-second change
395 gettimeofday(&t, NULL);
396 if (lt.tv_sec == 0) lt.tv_sec = t.tv_sec;
397 if (t.tv_sec != lt.tv_sec) break;
400 /* 2C. It is now time to collect the next sample. Discard any
401 any data in the input queue and get the sample. */
404 sample = Sample(coll.dd, coll.opts.trialsz);
406 nice(niceness); /* Collection done. Lower priority to normal. */
409 /* 2D. Okay, we're now in "quality time"--the slack after collection
410 of a sample until the time for the next sample arrives.
411 Now is an excellent time to do all kinds of housekeeping.
412 First of all, see if the sample just collected fits into
413 the packet currently being assembled. If not, we need to
414 save the last packet in the egg data file and initialise
415 a new packet for the time period in which this sample was
418 if ((savebuffer.records[0].timestamp == 0) ||
419 ((savebuffer.records[0].timestamp +
420 (savebuffer.hdr.sec_rec * savebuffer.hdr.rec_pkt)) <= t.tv_sec)) {
422 if (savebuffer.records[0].timestamp != 0) {
423 static int firstpacket = 1;
426 uint32 ptrials = 0, psum = 0;
427 static uint32 totalTrials = 0;
428 static double totalGrand = 0.0;
431 /* Sample isn't within current packet. Dump packet to the egg
432 data file and reinitialise to the window containing the
433 sample we just collected. But first, a little gimmick. If
434 this is the first packet we've collected and it contains
435 one or more missing samples at the beginning, discard it.
436 This keeps the routine missing samples in the first packet
437 after the egg starts up from being reported as missing
438 samples due to genuine synchronisation errors. */
440 if (!(firstpacket && (savebuffer.records[0].trials[0] == EGG_MISSING_DATA))) {
441 SavePacket(&savebuffer);
445 fprintf(stderr, "Dropping first packet to discard start-up missing samples.\n");
451 /* Update the last packet and grand mean data for the debug
452 interface status display. */
454 for (r = 0; r < savebuffer.hdr.rec_pkt; r++) {
455 for (t = 0; t < savebuffer.hdr.samp_rec; t++) {
456 if (savebuffer.records[r].trials[t] != EGG_MISSING_DATA) {
458 psum += savebuffer.records[r].trials[t];
462 mean_Packet = ((double) psum) / ptrials;
464 totalTrials += ptrials;
465 mean_Grand = totalGrand / totalTrials;
467 printf("Packets sent: %d Packet mean: %6.2f Grand mean: %6.2f\n",
468 total_Packets, mean_Packet, mean_Grand);
472 /* Check for change to the collection parameters...
473 now is the time to implement it. */
475 if (coll.opts.rec_pkt != protocol.rec_pkt ||
476 coll.opts.trialsz != protocol.trialsz ||
477 coll.opts.sec_rec != protocol.sec_rec ||
478 coll.opts.samp_rec != protocol.samp_rec) {
482 /* Reinitialise carton to interval containing sample */
483 resetCarton(t.tv_sec);
486 /* Store the sample into the carton, which is now guaranteed to
487 bracket the interval in which it was collected. */
490 /* But let's be sure it really *is* in the interval. */
491 if (t.tv_sec < savebuffer.records[0].timestamp ||
492 t.tv_sec >= (savebuffer.records[0].timestamp + savebuffer.hdr.sec_rec * savebuffer.hdr.rec_pkt)) {
493 fprintf(stderr, "***Sample, collected at %ld, is not within packet starting at %u.\n",
494 t.tv_sec, savebuffer.records[0].timestamp);
499 rindex = t.tv_sec - savebuffer.records[0].timestamp;
500 savebuffer.records[rindex / savebuffer.hdr.sec_rec].trials[rindex % savebuffer.hdr.sec_rec] = sample;
502 /* 2E. Update user interface. */
505 coll.data.trials[coll.sampct] = sample;
507 UIUpdate(coll.sampct >= coll.opts.samp_rec, &coll);
509 if (coll.sampct >= coll.opts.samp_rec) {
514 /* Now that the current sample has been dealt which, check
515 for egg-initiated actions whose time has come. */
517 /* 2F. First of all, it it's time to prod the basket with an
518 awake packet, lob one over the pole. */
520 if (getzulutime(NULL) - lastconn > (eggtable[0].connival * 60L)) {
521 lastsend = lastconn = getzulutime(NULL);
523 /* If this is a dial-and-drop egg, connect to the network before
524 sending the packet. */
526 if (sdlisten < 0 && eggtable[0].conntype == CONN_DND) {
527 sdlisten = NetUp(eggtable[0].upcmd, myaddr, myport);
533 /* 2G. If this is a dial-and-drop egg and it's been more than a
534 minute since we sent anything, tear down the network connection. */
536 if (eggtable[0].conntype == CONN_DND && sdlisten >= 0) {
537 if ((getzulutime(NULL) - lastsend) > 60L) {
538 NetDown(eggtable[0].dncmd, sdlisten);
543 /* Now deal with basket-initiated requests received on the socket
544 since the last time around the loop. To improve throughput for
545 dialup connections, we may come back here several times if we
546 have more data and if time allows. */
549 havedata = 0; /* Don't loop unless net activity happens. */
551 /* 1A. Wait for connection. */
552 if (eggtable[0].conntype == CONN_PERM) {
554 /* Initialize networking, get a listening socket at EGGPORT */
555 sdlisten = InitNetwork(myaddr, myport);
559 res = NetListen(sdlisten, &pktbuf, &rhost, FALSE);
560 if (res < 0 && res != ERR_COMM_TMOUT) {
561 fprintf(stderr, "NetListen error: %d\n", res);
562 break; /* Out of do loop */
564 /* Data present, process networking thread */
565 if (res == ERR_NONE) {
566 /* 1B. Validate connection.
567 Remote host address is in rhost. Make sure this is either an
568 egg or a basket, and set the flag appropriately. */
570 for (i = 0; i < numbaskets; i++) {
571 if (!memcmp(&(rhost.sin_addr),
572 &(baskettable[i].ipaddr.sin_addr),
573 sizeof(rhost.sin_addr))) {
575 remname = baskettable[i].name;
580 fprintf(stderr, "Attempt to connect from unknown source: %s",
581 sockaddr2dquad(&rhost));
582 break; /* Out of do loop */
585 havedata = 1; /* Initial assumption, until disproven. */
587 memcpy(&pkttype, pktbuf, sizeof(uint16));
588 memcpy(&pktsize, pktbuf+sizeof(uint16), sizeof(uint16));
589 pkttype = ntohs(pkttype);
590 pktsize = ntohs(pktsize);
592 /* 1C. Reply to connection. */
596 /* Not acceptable at an eggsite. */
597 fprintf(stderr, "%s: EGG could not accept %d packet\n",
601 /* Request for data from a basket. */
607 char *pktP = pktbuf + (2 * sizeof(uint16));
612 time_t stime_val = stime;
613 fprintf(stderr, "Request: eggid = %d, starttm = %u: %s\n",
614 reggid, stime, asctime(gmtime(&stime_val)));
616 res = LoadPacket(stime, reggid, &retrcart);
618 if (res == ERR_NONE) {
619 /* NetPacketize it */
620 MakeDataPkt(&outpktbuf, &retrcart);
621 rhost.sin_port = htons(BASKETPORT);
622 res = NetTalk(&rhost, outpktbuf, TRUE);
624 fprintf(stderr, "NetTalk failed (%d)\n", res);
627 lastDataSent = lastsend = getzulutime(NULL);
628 } else if (res == ERR_EOF) {
629 /* End of data. If DND, bring down connection */
630 if (eggtable[0].conntype == CONN_DND) {
631 NetDown(eggtable[0].dncmd, sdlisten);
638 case SETTINGS_PACKET:
639 /* Update settings in buffer, will change at next packet
642 { char *pktP = pktbuf + (3 * sizeof(uint16)) + sizeof(uint32);
644 unpack16(protocol.samp_rec);
645 unpack16(protocol.sec_rec);
646 unpack16(protocol.rec_pkt);
647 unpack8(protocol.trialsz);
650 fprintf(stderr, "Settings: samp_rec = %d, sec_rec = %d, rec_pkt = %d, trialsz = %d\n",
651 protocol.samp_rec, protocol.sec_rec, protocol.rec_pkt, protocol.trialsz);
658 gettimeofday(&ct, NULL);
659 } while (havedata && ct.tv_usec < 1000000L - MINSLEEP);
661 /* 2H. Compute the length in microseconds we should sleep before
662 the top of the next second. Note that we compute the interval
663 based on the time at the bottom of the loop, after dealing with
664 whatever housekeeping requests may have been performed since
665 collecting the last sample. */
667 gettimeofday(&ct, NULL);
668 if (lt.tv_sec == 0) {
669 lt.tv_sec = t.tv_sec - 1;
671 time_latency = (((t.tv_sec - lt.tv_sec) - 1) * 1000000L) + t.tv_usec;
672 time_housekeeping = ((ct.tv_sec - t.tv_sec) * 1000000L) + (ct.tv_usec - t.tv_usec);
677 printf("Sampling latency: %d.%03d msec", msec, usec);
679 usec = time_housekeeping;
682 printf(" Housekeeping time: %d.%03d msec\n", msec, usec);
685 sleeptime = ((1000000 - SLACK) - ct.tv_usec);
686 /* printf("Sleep time = %d usec\n", sleeptime); */
692 /* LoadRCFile -- Read in a configuration file. If the filename
693 argument is NULL, the default file is read.
694 Otherwise, configuration statements are read
695 from the file given by the argument. */
697 static void LoadRCFile(char *filename) {
700 char *myargv[MAX_PARSE], *tp;
701 int myargc, lcount, p, b, i;
703 if (filename != NULL) {
704 if ((fp = fopen(filename, "r")) == NULL) {
705 fprintf(stderr, "%s: Cannot open auxiliary configuration file %s.\n", pgmname, filename);
709 if ((fp = fopen(".eggrc", "r")) == NULL) {
710 if ((fp = fopen("~/.eggrc", "r")) == NULL) {
711 if ((fp = fopen("/etc/eggrc", "r")) == NULL) {
712 fprintf(stderr, "%s: Couldn't find a egg RC file.\n", pgmname);
721 while(fgets(linebuf, 200, fp) != NULL) {
722 /* Comments and blank lines ignored. */
724 if (*linebuf == '#' || *linebuf == '\n') continue;
725 Parse(linebuf, &myargc, myargv);
726 if (myargc == 0) continue;
727 if (!strcmp(myargv[0], "EGG")) {
728 if (myargc < 7 || myargc > 8) {
729 fprintf(stderr, "%s: RC error, line %d, poor %s format\n",
730 pgmname, lcount, myargv[0]);
734 fprintf(stderr, "%s: RC error, line %d, only one EGG allowed\n",
739 eggtable[0].name = mallocpy(myargv[1]);
740 eggtable[0].id = atoi(myargv[2]);
741 dquad2sockaddr(&(eggtable[0].ipaddr), NULL, myargv[3]);
742 eggtable[0].primbasket = mallocpy(myargv[4]);
743 eggtable[0].conntype = (!strcmp(myargv[5], "PERM"))?CONN_PERM:CONN_DND;
744 eggtable[0].connival = atoi(myargv[6]);
745 eggtable[0].url = NULL; /* URL specification is permitted, but ignored */
747 } else if (!strcmp(myargv[0], "BASKET")) {
749 fprintf(stderr, "%s: RC error, line %d, poor %s format\n",
750 pgmname, lcount, myargv[0]);
753 baskettable[numbaskets].name = mallocpy(myargv[1]);
754 dquad2sockaddr(&(baskettable[numbaskets].ipaddr), NULL, myargv[2]);
756 } else if (!strcmp(myargv[0], "PROTOCOL")) {
758 fprintf(stderr, "%s: RC error, line %d, poor %s format\n",
759 pgmname, lcount, myargv[0]);
762 protocol.samp_rec = atoi(myargv[1]);
763 protocol.sec_rec = atoi(myargv[2]);
764 protocol.rec_pkt = atoi(myargv[3]);
765 protocol.trialsz = atoi(myargv[4]);
767 /* REG: Configure Random Event Generator for this egg. */
769 } else if (!strcmp(myargv[0], "REG")) {
771 fprintf(stderr, "%s: RC error, line %d, poor %s format\n",
772 pgmname, lcount, myargv[0]);
775 if (configuredREG != NULL) {
776 fprintf(stderr, "%s: RC error, line %d, multiple REGs specified.\n",
780 for (i = 0; reg_table[i] != NULL; i++) {
781 if (strcasecmp(myargv[1], reg_table[i]->reg_name) == 0) {
782 configuredREG = reg_table[i];
783 devopts.type = reg_table[i]->reg_type;
786 if (configuredREG == NULL) {
787 fprintf(stderr, "%s: RC error, line %d, unknown REG type %s specified.\n",
788 pgmname, lcount, myargv[1]);
792 devopts.port = atoi(myargv[2]);
793 devopts.baud = atoi(myargv[3]);
794 } else if (!strcmp(myargv[0], "NETUP") ||
795 !strcmp(myargv[0], "NETDOWN") ||
796 !strcmp(myargv[0], "PORT") ||
797 !strcmp(myargv[0], "INTERFACE")) {
799 fprintf(stderr, "%s: RC error, line %d, poor %s format\n",
800 pgmname, lcount, myargv[0]);
803 tp = (char *) malloc(200);
804 for (*tp = 0, p = 1; p < myargc; p++) {
805 strcat(tp, myargv[p]);
806 if (p < myargc-1) strcat(tp, " ");
809 if (!strcmp(myargv[0], "NETUP")) eggtable[0].upcmd = tp;
810 else if (!strcmp(myargv[0], "NETDOWN")) eggtable[0].dncmd = tp;
811 else if (!strcmp(myargv[0], "PORT")) {
813 fprintf(stderr, "%s: RC error, ignoring bad port %d, using %d\n",
814 pgmname, atoi(tp), myport);
819 } else if (!strcmp(myargv[0], "INTERFACE")) myaddr = tp;
821 fprintf(stderr, "%s: RC error, %s is unknown keyword\n", pgmname, myargv[0]);
826 /* Consistency checks done only after loading the main
827 configuration file. */
829 if (filename == NULL) {
830 for (p = -1, b = 0; b < numbaskets; b++) {
831 if (!strcmp(baskettable[b].name, eggtable[0].primbasket)) p = b;
835 fprintf(stderr, "%s: RC error, primary basket should be listed first.\n", pgmname);
839 printf("Reloaded auxiliary configuration file %s.\n", filename);
844 /* MakeAwake -- Make a egg awake packet to let the basket
845 know we're on line. */
847 static void MakeAwake(AwakePacket *pkt) {
848 char *pktP = (char *) pkt;
850 pack16(AWAKE_PACKET);
851 pack16((4 * sizeof(uint16)) + sizeof(uint32));
852 pack16(eggtable[0].id);
853 pack32(getzulutime(NULL));
856 /* MakeDataPkt -- Build a canonical network byte order data packet
857 from the data collected in an EggCarton. */
859 static void MakeDataPkt(char **pkt, EggCarton *src) {
863 pktsize = (7 * sizeof(uint16)) + sizeof(trial) + /* Header */
864 (src->hdr.numrec * (sizeof(uint32) + /* Trial data */
865 src->hdr.samp_rec * sizeof(trial))) +
866 sizeof(uint16); /* CRC-16 */
867 *pkt = pktP = (char *) malloc(pktsize);
869 /* Assemble header fields into data packet. */
871 pack16(src->hdr.type = DATA_PACKET);
872 pack16(src->hdr.pktsize = pktsize);
873 pack16(src->hdr.eggid);
874 pack16(src->hdr.samp_rec);
875 pack16(src->hdr.sec_rec);
876 pack16(src->hdr.rec_pkt);
877 pack8(src->hdr.trialsz);
878 pack16(src->hdr.numrec);
880 /* Append data records to packet. */
882 for (rec = 0; rec < src->hdr.numrec; rec++) {
883 pack32(src->records[rec].timestamp);
884 pack8s(&(src->records[rec].trials), src->hdr.samp_rec);
887 /* No need to calculate CRC -- NetTalk does that for us.
888 Note that we did reserve space for the CRC when
889 allocating the packet buffer. */
892 /* SetCollOpts -- Set collection options from the protocol
893 specified in the .rc file or by a basket. */
895 static double SetCollOpts(void) {
898 coll.opts.eggid = eggtable[0].id;
900 coll.opts.rec_pkt = protocol.rec_pkt;
901 if (coll.opts.rec_pkt < 1) coll.opts.rec_pkt = 1;
902 if (coll.opts.rec_pkt > MAXREC_PKT) coll.opts.rec_pkt = MAXREC_PKT;
904 coll.opts.trialsz = protocol.trialsz;
905 if (coll.opts.trialsz < MINBITS) {
906 fprintf(stderr, "Attempt to set trial size below %d, to %d.\n",
907 MINBITS, coll.opts.trialsz);
908 coll.opts.trialsz = MINBITS;
910 if (coll.opts.trialsz > MAXBITS) {
911 fprintf(stderr, "Attempt to set trial size above %d, to %d.\n",
912 MAXBITS, coll.opts.trialsz);
913 coll.opts.trialsz = MAXBITS;
916 coll.opts.sec_rec = protocol.sec_rec;
917 if (coll.opts.sec_rec < 1) {
918 fprintf(stderr, "Attempt to set seconds/record below 1, to %d.\n",
920 coll.opts.sec_rec = 1;
922 if (coll.opts.sec_rec > MAXSEC_REC) {
923 fprintf(stderr, "Attempt to set seconds/record above %d, to %d.\n",
924 MAXSEC_REC, coll.opts.sec_rec);
925 coll.opts.sec_rec = MAXSEC_REC;
928 coll.opts.samp_rec = protocol.samp_rec;
929 if (coll.opts.samp_rec < 1) {
930 fprintf(stderr, "Attempt to set samples/record below 1, to %d.\n",
932 coll.opts.samp_rec = 1;
934 if (coll.opts.samp_rec > MAXSAMP_REC) {
935 fprintf(stderr, "Attempt to set samples/record above %d, to %d.\n",
936 MAXSAMP_REC, coll.opts.samp_rec);
937 coll.opts.samp_rec = MAXSAMP_REC;
940 sps = (double)coll.opts.samp_rec / (double)coll.opts.sec_rec;
942 fprintf(stderr, "Effective sample rate is about %f samp/sec or %f bits/sec\n",
943 sps, coll.opts.trialsz * sps);
944 fprintf(stderr, "Packets contain %d records\n", coll.opts.rec_pkt);
949 /* GreetBasket -- Send an awake packet to each configured
952 static int GreetBasket(void) {
954 struct sockaddr_in bhost;
959 for (b = 0; b < numbaskets; b++) {
960 memset(&bhost, 0, sizeof(struct sockaddr_in));
961 memcpy(&bhost, &(baskettable[b].ipaddr), sizeof(bhost));
962 bhost.sin_port = htons(BASKETPORT);
963 bhost.sin_family = AF_INET;
965 /* Don't gripe about network unreachable. Just means
966 network is down right now, try later. */
967 i = NetTalk(&bhost, (char *)&awake, FALSE);
969 /* Couldn't get to this one, try others. */
971 fprintf(stderr, "%s: Failure to reach basket '%s'\n",
972 pgmname, baskettable[b].name);
979 return ERR_COMM_TMOUT;
982 /* handle_sigkill -- KILL signal handler. Terminate user interface
985 static void handle_sigkill(int arg) {
990 /* handle_sighup -- HUP signal handler. Reload protocol from the
991 .eggprotocolrc file. */
993 static void handle_sighup(int arg) {
994 LoadRCFile(".eggprotocolrc");
995 #ifndef SIGACTION_WORKING
996 signal(SIGHUP, handle_sighup);