1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 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
}
|