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>
63 /* Good old Linux doesn't define the following functions
64 by default in the #include files the manual page claims
65 it does, and the conditional declarations with __USE_xx
66 doesn't work. Explicitly declare them, which doesn't
67 seem to do any harm on systems where these functions are
68 properly declared. If you get errors on the following
69 declarations, you can probably get around them by #ifdef-ing
70 the declarations off for your platform. */
72 extern int nice(int inc);
73 extern int strcasecmp(const char *s1, const char *s2);
75 /* Prototypes for forward functions. */
77 static void MakeAwake(AwakePacket *pkt);
78 static void MakeDataPkt(char **pkt, EggCarton *src);
79 static void LoadRCFile(char *filename);
80 static double SetCollOpts(void);
81 static int GreetBasket(void);
82 static void handle_sigkill(int arg), handle_sighup(int arg);
84 EggEntry eggtable[MAX_EGGS];
85 BasketEntry baskettable[MAX_BASKETS];
87 REG_driver *configuredREG = NULL; /* Configured REG driver */
89 short numeggs = 0, numbaskets = 0;
91 static DevOpts devopts; /* Device options for REG */
92 static CollectRecord coll;
93 static EggCarton savebuffer;
94 char *pgmname; /* Program name from argv[0] */
95 char *myaddr; /* Interface to bind */
96 int16 myport; /* Service port to bind */
97 int32 lastDataSent = 0; /* Time last packet sent to basket */
99 /* If no priority increment has been specified at compile time, set to
100 our default of ±10. [+/- 10, for folks without 8bit editors] */
105 static int niceness = NICE; /* Priority increment/decrement for collection */
107 /* Status exported to user interface. */
109 int32 time_latency = 0, time_housekeeping = 0;
112 static double mean_Packet = 0.0, mean_Grand = 0.0;
113 static int32 total_Packets = 0;
116 /* resetCarton -- Clear existing data from savebuffer and
117 reinitialise for a sampling interval
118 beginning at the specified start_time. */
120 static void resetCarton(uint32 start_time)
125 /* Save current collection options to packet. */
127 memcpy(&(savebuffer.hdr), &(coll.opts), sizeof(EggHeader));
129 /* Set number of records in packet and clear record
130 buffers to missing data. */
132 savebuffer.hdr.numrec = savebuffer.hdr.rec_pkt; /* Mark all records present */
134 /* Align start_time to even multiple of seconds per
137 sec_pkt = savebuffer.hdr.sec_rec * savebuffer.hdr.rec_pkt;
138 start_time = sec_pkt * (start_time / sec_pkt);
140 for (i = 0; i < savebuffer.hdr.rec_pkt; i++) {
141 savebuffer.records[i].timestamp = start_time;
142 start_time += savebuffer.hdr.sec_rec;
144 memset(savebuffer.records[i].trials, EGG_MISSING_DATA, MAXSAMP_REC);
146 #error "Can't represent MAXBITS in a byte value."
151 /* saveProtocol -- Save current protocol in the .eggprotocolrc file. */
153 static void saveProtocol(void)
157 rcfile = fopen(".eggprotocolrc", "w");
158 if (rcfile == NULL) {
159 fprintf(stderr, "%s: Cannot create .eggprotocolrc file.\n", pgmname);
162 fprintf(rcfile, "#\n# Current protocol for egg\n#\n");
163 fprintf(rcfile, "# PROTOCOL <samp_rec> <sec_rec> <rec_pkt> <trialsz>\n");
164 fprintf(rcfile, "PROTOCOL %d %d %d %d\n", protocol.samp_rec, protocol.sec_rec,
165 protocol.rec_pkt, protocol.trialsz);
169 static void Usage(void) {
170 printf("Usage: %s [-i interface] [-p port]\n", pgmname);
176 int main(int argc, char *argv[]) {
177 char *pktbuf, *outpktbuf;
178 uint16 pkttype, pktsize;
180 struct sockaddr_in rhost;
184 int32 lastconn, lastsend;
186 #ifdef SIGACTION_WORKING
187 struct sigaction sigact;
189 FILE *pidfile, *rcfile;
194 /* Defaults correspond to original usage */
199 if (argv[0][0] == '-') {
201 case 'i': /* Specify interface */
202 if (argc < 2) Usage();
206 case 'p': /* Specify port */
207 if (argc < 2) Usage();
208 myport = atoi(argv[1]);
209 if (myport < 0) Usage();
222 /* Solaris has an eccentric definition of sigaction() which
223 doesn't seem to even work according to Sun's own
224 documentation. (sa_flags is a 4 element array of
225 unsigned longs, with no mention of how one stores the
226 flags into it.) Let's just use plain old signal for
228 signal(SIGKILL, handle_sigkill);
229 signal(SIGHUP, handle_sighup);
231 #ifdef SIGACTION_WORKING
232 sigact.sa_handler = handle_sigkill;
235 sigact.sa_restorer = NULL;
236 sigaction(SIGKILL, &sigact, NULL);
238 sigact.sa_handler = handle_sighup;
239 sigaction(SIGHUP, &sigact, NULL);
241 signal(SIGKILL, handle_sigkill);
242 signal(SIGHUP, handle_sighup);
246 fprintf(stderr, "Starting egg %s.\n", Version);
248 /* Save our process ID in a file for folks who might wish
249 to send us a signal. */
251 pidfile = fopen("eggsample.pid", "w");
252 if (pidfile == NULL) {
253 fprintf(stderr, "Unable to create eggsample.pid file.\n");
256 fprintf(pidfile, "%d\n", getpid());
259 /* If we aren't being run by the super-user, disable the
260 priority raising and lowering mechanism. */
267 printf("User ID = %d. Niceness = %d.\n", getuid(), niceness);
268 printf("REG drivers installed: ");
269 for (i = 0; reg_table[i] != NULL; i++) {
270 printf(" %s", reg_table[i]->reg_name);
277 /* If an auxiliary configuration file exists, load it on
278 top of the arguments loaded from the main RC file. If
279 it doesn't exist, create it. */
281 if ((rcfile = fopen(".eggprotocolrc", "r")) != NULL) {
283 LoadRCFile(".eggprotocolrc");
288 if (configuredREG == NULL) {
289 fprintf(stderr, "%s: RC error, no REG specified for egg.\n",
295 printf("REG configured: %s = %d, %d, %ld\n", configuredREG ->reg_name,
296 devopts.type, devopts.port, devopts.baud);
303 if ((coll.dd = OpenDev(&devopts)) < 0) {
304 fprintf(stderr, "Couldn't talk to hardware device.\n");
308 if (EvalSpeed(coll.dd) < coll.opts.trialsz * sps) {
309 fprintf(stderr, "Requested speed exceeds device capabilities.\n");
315 fprintf(stderr, "TCP configured: %s.%d\n", myaddr, myport);
317 fprintf(stderr, "TCP configured: hostname.%d\n", myport);
323 sdlisten = -1; /* No conn yet. */
324 lastconn = lastsend = 0;
326 /* Initialise carton to zero time, indicating no data in
330 /* Initialize storage system */
331 InitStorage("%Y-%m/%Y-%m-%d-$06E");
333 /* Inner loop is currently two threads which are both processed
334 together through NBIO.
336 2A. Adjust priority and sleep until time computed below (2H).
337 2B. Busy wait until second ticks over.
338 2C. Empty REG input buffer (if needed), then collect sample.
339 2D. Lower priority to handle bookkeeping; save packet.
340 2E. Update user interface.
342 2F. Send awake packet if connival is passed.
343 2G. [DND only] Put net to sleep if we've been waiting more than a minute
345 1A. Wait for connection. When a connection comes in,
346 packet has been decrypted and verified.
347 1B. Validate connection.
348 1C. Reply to connection.
349 1D. If we have more than MINSLEEP time left in second,
352 2H. Compute new sleep time.
356 static int sleeptime = 0;
357 static struct timeval t, lt = {0, 0};
367 #define SLACK 50000 /* CPU loop resynchronisation time in microseconds */
371 #define MINSLEEP 100000 /* Minimum sleep time, usec */
374 /* 2A. If we have successfully computed a sleep time until
375 the window opens to collect the next sample, give up
376 the CPU until that time arrives. */
379 nice(-niceness); /* Raise priority for re-dispatch */
382 Usleep((unsigned) sleeptime);
385 /* 2B. We're now close to the start of the next second. Watch the
386 time until the second changes, then collect the next sample.
387 Note that this method of doing things enforces the one
388 sample/second that has become standard practise. The code at
389 the bottom of the loop could be changed to allow for a longer
390 interval, but this code enforces a minimum of a 1-second change
394 gettimeofday(&t, NULL);
395 if (lt.tv_sec == 0) lt.tv_sec = t.tv_sec;
396 if (t.tv_sec != lt.tv_sec) break;
399 /* 2C. It is now time to collect the next sample. Discard any
400 any data in the input queue and get the sample. */
403 sample = Sample(coll.dd, coll.opts.trialsz);
405 nice(niceness); /* Collection done. Lower priority to normal. */
408 /* 2D. Okay, we're now in "quality time"--the slack after collection
409 of a sample until the time for the next sample arrives.
410 Now is an excellent time to do all kinds of housekeeping.
411 First of all, see if the sample just collected fits into
412 the packet currently being assembled. If not, we need to
413 save the last packet in the egg data file and initialise
414 a new packet for the time period in which this sample was
417 if ((savebuffer.records[0].timestamp == 0) ||
418 ((savebuffer.records[0].timestamp +
419 (savebuffer.hdr.sec_rec * savebuffer.hdr.rec_pkt)) <= t.tv_sec)) {
421 if (savebuffer.records[0].timestamp != 0) {
422 static int firstpacket = 1;
425 uint32 ptrials = 0, psum = 0;
426 static uint32 totalTrials = 0;
427 static double totalGrand = 0.0;
430 /* Sample isn't within current packet. Dump packet to the egg
431 data file and reinitialise to the window containing the
432 sample we just collected. But first, a little gimmick. If
433 this is the first packet we've collected and it contains
434 one or more missing samples at the beginning, discard it.
435 This keeps the routine missing samples in the first packet
436 after the egg starts up from being reported as missing
437 samples due to genuine synchronisation errors. */
439 if (!(firstpacket && (savebuffer.records[0].trials[0] == EGG_MISSING_DATA))) {
440 SavePacket(&savebuffer);
444 fprintf(stderr, "Dropping first packet to discard start-up missing samples.\n");
450 /* Update the last packet and grand mean data for the debug
451 interface status display. */
453 for (r = 0; r < savebuffer.hdr.rec_pkt; r++) {
454 for (t = 0; t < savebuffer.hdr.samp_rec; t++) {
455 if (savebuffer.records[r].trials[t] != EGG_MISSING_DATA) {
457 psum += savebuffer.records[r].trials[t];
461 mean_Packet = ((double) psum) / ptrials;
463 totalTrials += ptrials;
464 mean_Grand = totalGrand / totalTrials;
466 printf("Packets sent: %ld Packet mean: %6.2f Grand mean: %6.2f\n",
467 total_Packets, mean_Packet, mean_Grand);
471 /* Check for change to the collection parameters...
472 now is the time to implement it. */
474 if (coll.opts.rec_pkt != protocol.rec_pkt ||
475 coll.opts.trialsz != protocol.trialsz ||
476 coll.opts.sec_rec != protocol.sec_rec ||
477 coll.opts.samp_rec != protocol.samp_rec) {
481 /* Reinitialise carton to interval containing sample */
482 resetCarton(t.tv_sec);
485 /* Store the sample into the carton, which is now guaranteed to
486 bracket the interval in which it was collected. */
489 /* But let's be sure it really *is* in the interval. */
490 if (t.tv_sec < savebuffer.records[0].timestamp ||
491 t.tv_sec >= (savebuffer.records[0].timestamp + savebuffer.hdr.sec_rec * savebuffer.hdr.rec_pkt)) {
492 fprintf(stderr, "***Sample, collected at %ld, is not within packet starting at %ld.\n",
493 t.tv_sec, savebuffer.records[0].timestamp);
498 rindex = t.tv_sec - savebuffer.records[0].timestamp;
499 savebuffer.records[rindex / savebuffer.hdr.sec_rec].trials[rindex % savebuffer.hdr.sec_rec] = sample;
501 /* 2E. Update user interface. */
504 coll.data.trials[coll.sampct] = sample;
506 UIUpdate(coll.sampct >= coll.opts.samp_rec, &coll);
508 if (coll.sampct >= coll.opts.samp_rec) {
513 /* Now that the current sample has been dealt which, check
514 for egg-initiated actions whose time has come. */
516 /* 2F. First of all, it it's time to prod the basket with an
517 awake packet, lob one over the pole. */
519 if (getzulutime(NULL) - lastconn > (eggtable[0].connival * 60L)) {
520 lastsend = lastconn = getzulutime(NULL);
522 /* If this is a dial-and-drop egg, connect to the network before
523 sending the packet. */
525 if (sdlisten < 0 && eggtable[0].conntype == CONN_DND) {
526 sdlisten = NetUp(eggtable[0].upcmd, myaddr, myport);
532 /* 2G. If this is a dial-and-drop egg and it's been more than a
533 minute since we sent anything, tear down the network connection. */
535 if (eggtable[0].conntype == CONN_DND && sdlisten >= 0) {
536 if ((getzulutime(NULL) - lastsend) > 60L) {
537 NetDown(eggtable[0].dncmd, sdlisten);
542 /* Now deal with basket-initiated requests received on the socket
543 since the last time around the loop. To improve throughput for
544 dialup connections, we may come back here several times if we
545 have more data and if time allows. */
548 havedata = 0; /* Don't loop unless net activity happens. */
550 /* 1A. Wait for connection. */
551 if (eggtable[0].conntype == CONN_PERM) {
553 /* Initialize networking, get a listening socket at EGGPORT */
554 sdlisten = InitNetwork(myaddr, myport);
558 res = NetListen(sdlisten, &pktbuf, &rhost, FALSE);
559 if (res < 0 && res != ERR_COMM_TMOUT) {
560 fprintf(stderr, "NetListen error: %d\n", res);
561 break; /* Out of do loop */
563 /* Data present, process networking thread */
564 if (res == ERR_NONE) {
565 /* 1B. Validate connection.
566 Remote host address is in rhost. Make sure this is either an
567 egg or a basket, and set the flag appropriately. */
569 for (i = 0; i < numbaskets; i++) {
570 if (!memcmp(&(rhost.sin_addr),
571 &(baskettable[i].ipaddr.sin_addr),
572 sizeof(rhost.sin_addr))) {
574 remname = baskettable[i].name;
579 fprintf(stderr, "Attempt to connect from unknown source: %s",
580 sockaddr2dquad(&rhost));
581 break; /* Out of do loop */
584 havedata = 1; /* Initial assumption, until disproven. */
586 memcpy(&pkttype, pktbuf, sizeof(uint16));
587 memcpy(&pktsize, pktbuf+sizeof(uint16), sizeof(uint16));
588 pkttype = ntohs(pkttype);
589 pktsize = ntohs(pktsize);
591 /* 1C. Reply to connection. */
595 /* Not acceptable at an eggsite. */
596 fprintf(stderr, "%s: EGG could not accept %d packet\n",
600 /* Request for data from a basket. */
606 char *pktP = pktbuf + (2 * sizeof(uint16));
611 fprintf(stderr, "Request: eggid = %d, starttm = %ld: %s",
612 reggid, stime, asctime(gmtime((time_t *) &stime)));
614 res = LoadPacket(stime, reggid, &retrcart);
616 if (res == ERR_NONE) {
617 /* NetPacketize it */
618 MakeDataPkt(&outpktbuf, &retrcart);
619 rhost.sin_port = htons(BASKETPORT);
620 res = NetTalk(&rhost, outpktbuf, TRUE);
622 fprintf(stderr, "NetTalk failed (%d)\n", res);
625 lastDataSent = lastsend = getzulutime(NULL);
626 } else if (res == ERR_EOF) {
627 /* End of data. If DND, bring down connection */
628 if (eggtable[0].conntype == CONN_DND) {
629 NetDown(eggtable[0].dncmd, sdlisten);
636 case SETTINGS_PACKET:
637 /* Update settings in buffer, will change at next packet
640 { char *pktP = pktbuf + (3 * sizeof(uint16)) + sizeof(uint32);
642 unpackShort(protocol.samp_rec);
643 unpackShort(protocol.sec_rec);
644 unpackShort(protocol.rec_pkt);
645 unpackByte(protocol.trialsz);
648 fprintf(stderr, "Settings: samp_rec = %d, sec_rec = %d, rec_pkt = %d, trialsz = %d\n",
649 protocol.samp_rec, protocol.sec_rec, protocol.rec_pkt, protocol.trialsz);
656 gettimeofday(&ct, NULL);
657 } while (havedata && ct.tv_usec < 1000000L - MINSLEEP);
659 /* 2H. Compute the length in microseconds we should sleep before
660 the top of the next second. Note that we compute the interval
661 based on the time at the bottom of the loop, after dealing with
662 whatever housekeeping requests may have been performed since
663 collecting the last sample. */
665 gettimeofday(&ct, NULL);
666 if (lt.tv_sec == 0) {
667 lt.tv_sec = t.tv_sec - 1;
669 time_latency = (((t.tv_sec - lt.tv_sec) - 1) * 1000000L) + t.tv_usec;
670 time_housekeeping = ((ct.tv_sec - t.tv_sec) * 1000000L) + (ct.tv_usec - t.tv_usec);
675 printf("Sampling latency: %ld.%03ld msec", msec, usec);
677 usec = time_housekeeping;
680 printf(" Housekeeping time: %ld.%03ld msec\n", msec, usec);
683 sleeptime = ((1000000 - SLACK) - ct.tv_usec);
684 /* printf("Sleep time = %d usec\n", sleeptime); */
690 /* LoadRCFile -- Read in a configuration file. If the filename
691 argument is NULL, the default file is read.
692 Otherwise, configuration statements are read
693 from the file given by the argument. */
695 static void LoadRCFile(char *filename) {
698 char *myargv[MAX_PARSE], *tp;
699 int myargc, lcount, p, b, i;
701 if (filename != NULL) {
702 if ((fp = fopen(filename, "r")) == NULL) {
703 fprintf(stderr, "%s: Cannot open auxiliary configuration file %s.\n", pgmname, filename);
707 if ((fp = fopen(".eggrc", "r")) == NULL) {
708 if ((fp = fopen("~/.eggrc", "r")) == NULL) {
709 if ((fp = fopen("/etc/eggrc", "r")) == NULL) {
710 fprintf(stderr, "%s: Couldn't find a egg RC file.\n", pgmname);
719 while(fgets(linebuf, 200, fp) != NULL) {
720 /* Comments and blank lines ignored. */
722 if (*linebuf == '#' || *linebuf == '\n') continue;
723 Parse(linebuf, &myargc, myargv);
724 if (myargc == 0) continue;
725 if (!strcmp(myargv[0], "EGG")) {
726 if (myargc < 7 || myargc > 8) {
727 fprintf(stderr, "%s: RC error, line %d, poor %s format\n",
728 pgmname, lcount, myargv[0]);
732 fprintf(stderr, "%s: RC error, line %d, only one EGG allowed\n",
737 eggtable[0].name = mallocpy(myargv[1]);
738 eggtable[0].id = atoi(myargv[2]);
739 dquad2sockaddr(&(eggtable[0].ipaddr), NULL, myargv[3]);
740 eggtable[0].primbasket = mallocpy(myargv[4]);
741 eggtable[0].conntype = (!strcmp(myargv[5], "PERM"))?CONN_PERM:CONN_DND;
742 eggtable[0].connival = atoi(myargv[6]);
743 eggtable[0].url = NULL; /* URL specification is permitted, but ignored */
745 } else if (!strcmp(myargv[0], "BASKET")) {
747 fprintf(stderr, "%s: RC error, line %d, poor %s format\n",
748 pgmname, lcount, myargv[0]);
751 baskettable[numbaskets].name = mallocpy(myargv[1]);
752 dquad2sockaddr(&(baskettable[numbaskets].ipaddr), NULL, myargv[2]);
754 } else if (!strcmp(myargv[0], "PROTOCOL")) {
756 fprintf(stderr, "%s: RC error, line %d, poor %s format\n",
757 pgmname, lcount, myargv[0]);
760 protocol.samp_rec = atoi(myargv[1]);
761 protocol.sec_rec = atoi(myargv[2]);
762 protocol.rec_pkt = atoi(myargv[3]);
763 protocol.trialsz = atoi(myargv[4]);
765 /* REG: Configure Random Event Generator for this egg. */
767 } else if (!strcmp(myargv[0], "REG")) {
769 fprintf(stderr, "%s: RC error, line %d, poor %s format\n",
770 pgmname, lcount, myargv[0]);
773 if (configuredREG != NULL) {
774 fprintf(stderr, "%s: RC error, line %d, multiple REGs specified.\n",
778 for (i = 0; reg_table[i] != NULL; i++) {
779 if (strcasecmp(myargv[1], reg_table[i]->reg_name) == 0) {
780 configuredREG = reg_table[i];
781 devopts.type = reg_table[i]->reg_type;
784 if (configuredREG == NULL) {
785 fprintf(stderr, "%s: RC error, line %d, unknown REG type %s specified.\n",
786 pgmname, lcount, myargv[1]);
790 devopts.port = atoi(myargv[2]);
791 devopts.baud = atoi(myargv[3]);
792 } else if (!strcmp(myargv[0], "NETUP") ||
793 !strcmp(myargv[0], "NETDOWN") ||
794 !strcmp(myargv[0], "PORT") ||
795 !strcmp(myargv[0], "INTERFACE")) {
797 fprintf(stderr, "%s: RC error, line %d, poor %s format\n",
798 pgmname, lcount, myargv[0]);
801 tp = (char *) malloc(200);
802 for (*tp = 0, p = 1; p < myargc; p++) {
803 strcat(tp, myargv[p]);
804 if (p < myargc-1) strcat(tp, " ");
807 if (!strcmp(myargv[0], "NETUP")) eggtable[0].upcmd = tp;
808 else if (!strcmp(myargv[0], "NETDOWN")) eggtable[0].dncmd = tp;
809 else if (!strcmp(myargv[0], "PORT")) {
811 fprintf(stderr, "%s: RC error, ignoring bad port %d, using %d\n",
812 pgmname, atoi(tp), myport);
817 } else if (!strcmp(myargv[0], "INTERFACE")) myaddr = tp;
819 fprintf(stderr, "%s: RC error, %s is unknown keyword\n", pgmname, myargv[0]);
824 /* Consistency checks done only after loading the main
825 configuration file. */
827 if (filename == NULL) {
828 for (p = -1, b = 0; b < numbaskets; b++) {
829 if (!strcmp(baskettable[b].name, eggtable[0].primbasket)) p = b;
833 fprintf(stderr, "%s: RC error, primary basket should be listed first.\n", pgmname);
837 printf("Reloaded auxiliary configuration file %s.\n", filename);
842 /* MakeAwake -- Make a egg awake packet to let the basket
843 know we're on line. */
845 static void MakeAwake(AwakePacket *pkt) {
846 char *pktP = (char *) pkt;
848 packShort(AWAKE_PACKET);
849 packShort((4 * sizeof(uint16)) + sizeof(uint32));
850 packShort(eggtable[0].id);
851 packLong(getzulutime(NULL));
854 /* MakeDataPkt -- Build a canonical network byte order data packet
855 from the data collected in an EggCarton. */
857 static void MakeDataPkt(char **pkt, EggCarton *src) {
861 pktsize = (7 * sizeof(uint16)) + sizeof(trial) + /* Header */
862 (src->hdr.numrec * (sizeof(uint32) + /* Trial data */
863 src->hdr.samp_rec * sizeof(trial))) +
864 sizeof(uint16); /* CRC-16 */
865 *pkt = pktP = (char *) malloc(pktsize);
867 /* Assemble header fields into data packet. */
869 packShort(src->hdr.type = DATA_PACKET);
870 packShort(src->hdr.pktsize = pktsize);
871 packShort(src->hdr.eggid);
872 packShort(src->hdr.samp_rec);
873 packShort(src->hdr.sec_rec);
874 packShort(src->hdr.rec_pkt);
875 packByte(src->hdr.trialsz);
876 packShort(src->hdr.numrec);
878 /* Append data records to packet. */
880 for (rec = 0; rec < src->hdr.numrec; rec++) {
881 packLong(src->records[rec].timestamp);
882 packBytes(&(src->records[rec].trials), src->hdr.samp_rec);
885 /* No need to calculate CRC -- NetTalk does that for us.
886 Note that we did reserve space for the CRC when
887 allocating the packet buffer. */
890 /* SetCollOpts -- Set collection options from the protocol
891 specified in the .rc file or by a basket. */
893 static double SetCollOpts(void) {
896 coll.opts.eggid = eggtable[0].id;
898 coll.opts.rec_pkt = protocol.rec_pkt;
899 if (coll.opts.rec_pkt < 1) coll.opts.rec_pkt = 1;
900 if (coll.opts.rec_pkt > MAXREC_PKT) coll.opts.rec_pkt = MAXREC_PKT;
902 coll.opts.trialsz = protocol.trialsz;
903 if (coll.opts.trialsz < MINBITS) {
904 fprintf(stderr, "Attempt to set trial size below %d, to %d.\n",
905 MINBITS, coll.opts.trialsz);
906 coll.opts.trialsz = MINBITS;
908 if (coll.opts.trialsz > MAXBITS) {
909 fprintf(stderr, "Attempt to set trial size above %d, to %d.\n",
910 MAXBITS, coll.opts.trialsz);
911 coll.opts.trialsz = MAXBITS;
914 coll.opts.sec_rec = protocol.sec_rec;
915 if (coll.opts.sec_rec < 1) {
916 fprintf(stderr, "Attempt to set seconds/record below 1, to %d.\n",
918 coll.opts.sec_rec = 1;
920 if (coll.opts.sec_rec > MAXSEC_REC) {
921 fprintf(stderr, "Attempt to set seconds/record above %d, to %d.\n",
922 MAXSEC_REC, coll.opts.sec_rec);
923 coll.opts.sec_rec = MAXSEC_REC;
926 coll.opts.samp_rec = protocol.samp_rec;
927 if (coll.opts.samp_rec < 1) {
928 fprintf(stderr, "Attempt to set samples/record below 1, to %d.\n",
930 coll.opts.samp_rec = 1;
932 if (coll.opts.samp_rec > MAXSAMP_REC) {
933 fprintf(stderr, "Attempt to set samples/record above %d, to %d.\n",
934 MAXSAMP_REC, coll.opts.samp_rec);
935 coll.opts.samp_rec = MAXSAMP_REC;
938 sps = (double)coll.opts.samp_rec / (double)coll.opts.sec_rec;
940 fprintf(stderr, "Effective sample rate is about %f samp/sec or %f bits/sec\n",
941 sps, coll.opts.trialsz * sps);
942 fprintf(stderr, "Packets contain %d records\n", coll.opts.rec_pkt);
947 /* GreetBasket -- Send an awake packet to each configured
950 static int GreetBasket(void) {
952 struct sockaddr_in bhost;
957 for (b = 0; b < numbaskets; b++) {
958 memset(&bhost, 0, sizeof(struct sockaddr_in));
959 memcpy(&bhost, &(baskettable[b].ipaddr), sizeof(bhost));
960 bhost.sin_port = htons(BASKETPORT);
961 bhost.sin_family = AF_INET;
963 /* Don't gripe about network unreachable. Just means
964 network is down right now, try later. */
965 i = NetTalk(&bhost, (char *)&awake, FALSE);
967 /* Couldn't get to this one, try others. */
969 fprintf(stderr, "%s: Failure to reach basket '%s'\n",
970 pgmname, baskettable[b].name);
977 return ERR_COMM_TMOUT;
980 /* handle_sigkill -- KILL signal handler. Terminate user interface
983 static void handle_sigkill(int arg) {
988 /* handle_sighup -- HUP signal handler. Reload protocol from the
989 .eggprotocolrc file. */
991 static void handle_sighup(int arg) {
992 LoadRCFile(".eggprotocolrc");
993 #ifndef SIGACTION_WORKING
994 signal(SIGHUP, handle_sighup);