ntp_util.c revision 132452
1/* 2 * ntp_util.c - stuff I didn't have any other place for 3 */ 4 5#ifdef HAVE_CONFIG_H 6# include <config.h> 7#endif 8 9#include "ntpd.h" 10#include "ntp_io.h" 11#include "ntp_unixtime.h" 12#include "ntp_filegen.h" 13#include "ntp_if.h" 14#include "ntp_stdlib.h" 15 16#include <stdio.h> 17#include <ctype.h> 18#include <sys/types.h> 19#ifdef HAVE_SYS_IOCTL_H 20# include <sys/ioctl.h> 21#endif 22 23#ifdef HAVE_IEEEFP_H 24# include <ieeefp.h> 25#endif 26#ifdef HAVE_MATH_H 27# include <math.h> 28#endif 29 30#ifdef DOSYNCTODR 31#if !defined(VMS) 32#include <sys/resource.h> 33#endif /* VMS */ 34#endif 35 36#if defined(VMS) 37#include <descrip.h> 38#endif /* VMS */ 39 40/* 41 * This contains odds and ends. Right now the only thing you'll find 42 * in here is the hourly stats printer and some code to support 43 * rereading the keys file, but I may eventually put other things in 44 * here such as code to do something with the leap bits. 45 */ 46/* 47 * Name of the keys file 48 */ 49static char *key_file_name; 50 51/* 52 * The name of the drift_comp file and the temporary. 53 */ 54static char *stats_drift_file; 55static char *stats_temp_file; 56 57/* 58 * Statistics file stuff 59 */ 60#ifndef NTP_VAR 61#ifndef SYS_WINNT 62#define NTP_VAR "/var/NTP/" /* NOTE the trailing '/' */ 63#else 64#define NTP_VAR "c:\\var\\ntp\\" /* NOTE the trailing '\\' */ 65#endif /* SYS_WINNT */ 66#endif 67 68#ifndef MAXPATHLEN 69#define MAXPATHLEN 256 70#endif 71 72static char statsdir[MAXPATHLEN] = NTP_VAR; 73 74static FILEGEN peerstats; 75static FILEGEN loopstats; 76static FILEGEN clockstats; 77static FILEGEN rawstats; 78static FILEGEN sysstats; 79#ifdef OPENSSL 80static FILEGEN cryptostats; 81#endif /* OPENSSL */ 82 83/* 84 * This controls whether stats are written to the fileset. Provided 85 * so that ntpdc can turn off stats when the file system fills up. 86 */ 87int stats_control; 88 89/* 90 * init_util - initialize the utilities 91 */ 92void 93init_util(void) 94{ 95 stats_drift_file = 0; 96 stats_temp_file = 0; 97 key_file_name = 0; 98 99#define PEERNAME "peerstats" 100#define LOOPNAME "loopstats" 101#define CLOCKNAME "clockstats" 102#define RAWNAME "rawstats" 103#define STANAME "systats" 104#ifdef OPENSSL 105#define CRYPTONAME "cryptostats" 106#endif /* OPENSSL */ 107 108 peerstats.fp = NULL; 109 peerstats.prefix = &statsdir[0]; 110 peerstats.basename = (char*)emalloc(strlen(PEERNAME)+1); 111 strcpy(peerstats.basename, PEERNAME); 112 peerstats.id = 0; 113 peerstats.type = FILEGEN_DAY; 114 peerstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/ 115 filegen_register("peerstats", &peerstats); 116 117 loopstats.fp = NULL; 118 loopstats.prefix = &statsdir[0]; 119 loopstats.basename = (char*)emalloc(strlen(LOOPNAME)+1); 120 strcpy(loopstats.basename, LOOPNAME); 121 loopstats.id = 0; 122 loopstats.type = FILEGEN_DAY; 123 loopstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/ 124 filegen_register("loopstats", &loopstats); 125 126 clockstats.fp = NULL; 127 clockstats.prefix = &statsdir[0]; 128 clockstats.basename = (char*)emalloc(strlen(CLOCKNAME)+1); 129 strcpy(clockstats.basename, CLOCKNAME); 130 clockstats.id = 0; 131 clockstats.type = FILEGEN_DAY; 132 clockstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/ 133 filegen_register("clockstats", &clockstats); 134 135 rawstats.fp = NULL; 136 rawstats.prefix = &statsdir[0]; 137 rawstats.basename = (char*)emalloc(strlen(RAWNAME)+1); 138 strcpy(rawstats.basename, RAWNAME); 139 rawstats.id = 0; 140 rawstats.type = FILEGEN_DAY; 141 rawstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/ 142 filegen_register("rawstats", &rawstats); 143 144 sysstats.fp = NULL; 145 sysstats.prefix = &statsdir[0]; 146 sysstats.basename = (char*)emalloc(strlen(STANAME)+1); 147 strcpy(sysstats.basename, STANAME); 148 sysstats.id = 0; 149 sysstats.type = FILEGEN_DAY; 150 sysstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/ 151 filegen_register("sysstats", &sysstats); 152 153#ifdef OPENSSL 154 cryptostats.fp = NULL; 155 cryptostats.prefix = &statsdir[0]; 156 cryptostats.basename = (char*)emalloc(strlen(CRYPTONAME)+1); 157 strcpy(cryptostats.basename, CRYPTONAME); 158 cryptostats.id = 0; 159 cryptostats.type = FILEGEN_DAY; 160 cryptostats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/ 161 filegen_register("cryptostats", &cryptostats); 162#endif /* OPENSSL */ 163 164#undef PEERNAME 165#undef LOOPNAME 166#undef CLOCKNAME 167#undef RAWNAME 168#undef STANAME 169#ifdef OPENSSL 170#undef CRYPTONAME 171#endif /* OPENSSL */ 172} 173 174 175/* 176 * hourly_stats - print some interesting stats 177 */ 178void 179hourly_stats(void) 180{ 181 FILE *fp; 182 183#ifdef DOSYNCTODR 184 struct timeval tv; 185#if !defined(VMS) 186 int prio_set; 187#endif 188#ifdef HAVE_GETCLOCK 189 struct timespec ts; 190#endif 191 int o_prio; 192 193 /* 194 * Sometimes having a Sun can be a drag. 195 * 196 * The kernel variable dosynctodr controls whether the system's 197 * soft clock is kept in sync with the battery clock. If it 198 * is zero, then the soft clock is not synced, and the battery 199 * clock is simply left to rot. That means that when the system 200 * reboots, the battery clock (which has probably gone wacky) 201 * sets the soft clock. That means ntpd starts off with a very 202 * confused idea of what time it is. It then takes a large 203 * amount of time to figure out just how wacky the battery clock 204 * has made things drift, etc, etc. The solution is to make the 205 * battery clock sync up to system time. The way to do THAT is 206 * to simply set the time of day to the current time of day, but 207 * as quickly as possible. This may, or may not be a sensible 208 * thing to do. 209 * 210 * CAVEAT: settimeofday() steps the sun clock by about 800 us, 211 * so setting DOSYNCTODR seems a bad idea in the 212 * case of us resolution 213 */ 214 215#if !defined(VMS) 216 /* (prr) getpriority returns -1 on error, but -1 is also a valid 217 * return value (!), so instead we have to zero errno before the 218 * call and check it for non-zero afterwards. 219 */ 220 errno = 0; 221 prio_set = 0; 222 o_prio = getpriority(PRIO_PROCESS,0); /* Save setting */ 223 224 /* 225 * (prr) if getpriority succeeded, call setpriority to raise 226 * scheduling priority as high as possible. If that succeeds 227 * as well, set the prio_set flag so we remember to reset 228 * priority to its previous value below. Note that on Solaris 229 * 2.6 (and beyond?), both getpriority and setpriority will fail 230 * with ESRCH, because sched_setscheduler (called from main) put 231 * us in the real-time scheduling class which setpriority 232 * doesn't know about. Being in the real-time class is better 233 * than anything setpriority can do, anyhow, so this error is 234 * silently ignored. 235 */ 236 if ((errno == 0) && (setpriority(PRIO_PROCESS,0,-20) == 0)) 237 prio_set = 1; /* overdrive */ 238#endif /* VMS */ 239#ifdef HAVE_GETCLOCK 240 (void) getclock(TIMEOFDAY, &ts); 241 tv.tv_sec = ts.tv_sec; 242 tv.tv_usec = ts.tv_nsec / 1000; 243#else /* not HAVE_GETCLOCK */ 244 GETTIMEOFDAY(&tv,(struct timezone *)NULL); 245#endif /* not HAVE_GETCLOCK */ 246 if (ntp_set_tod(&tv,(struct timezone *)NULL) != 0) { 247 msyslog(LOG_ERR, "can't sync battery time: %m"); 248 } 249#if !defined(VMS) 250 if (prio_set) 251 setpriority(PRIO_PROCESS, 0, o_prio); /* downshift */ 252#endif /* VMS */ 253#endif /* DOSYNCTODR */ 254 255 NLOG(NLOG_SYSSTATIST) 256 msyslog(LOG_INFO, 257 "offset %.6f sec freq %.3f ppm error %.6f poll %d", 258 last_offset, drift_comp * 1e6, sys_jitter, 259 sys_poll); 260 261 262 record_sys_stats(); 263 if (stats_drift_file != 0) { 264 if ((fp = fopen(stats_temp_file, "w")) == NULL) { 265 msyslog(LOG_ERR, "can't open %s: %m", 266 stats_temp_file); 267 return; 268 } 269 fprintf(fp, "%.3f\n", drift_comp * 1e6); 270 (void)fclose(fp); 271 /* atomic */ 272#ifdef SYS_WINNT 273 (void) _unlink(stats_drift_file); /* rename semantics differ under NT */ 274#endif /* SYS_WINNT */ 275 276#ifndef NO_RENAME 277 (void) rename(stats_temp_file, stats_drift_file); 278#else 279 /* we have no rename NFS of ftp in use*/ 280 if ((fp = fopen(stats_drift_file, "w")) == NULL) { 281 msyslog(LOG_ERR, "can't open %s: %m", 282 stats_drift_file); 283 return; 284 } 285 286#endif 287 288#if defined(VMS) 289 /* PURGE */ 290 { 291 $DESCRIPTOR(oldvers,";-1"); 292 struct dsc$descriptor driftdsc = { 293 strlen(stats_drift_file),0,0,stats_drift_file }; 294 295 while(lib$delete_file(&oldvers,&driftdsc) & 1) ; 296 } 297#endif 298 } 299} 300 301 302/* 303 * stats_config - configure the stats operation 304 */ 305void 306stats_config( 307 int item, 308 char *invalue /* only one type so far */ 309 ) 310{ 311 FILE *fp; 312 char *value; 313 double old_drift; 314 int len; 315 316 /* 317 * Expand environment strings under Windows NT, since the 318 * command interpreter doesn't do this, the program must. 319 */ 320#ifdef SYS_WINNT 321 char newvalue[MAX_PATH], parameter[MAX_PATH]; 322 323 if (!ExpandEnvironmentStrings(invalue, newvalue, MAX_PATH)) { 324 switch(item) { 325 case STATS_FREQ_FILE: 326 strcpy(parameter,"STATS_FREQ_FILE"); 327 break; 328 case STATS_STATSDIR: 329 strcpy(parameter,"STATS_STATSDIR"); 330 break; 331 case STATS_PID_FILE: 332 strcpy(parameter,"STATS_PID_FILE"); 333 break; 334 default: 335 strcpy(parameter,"UNKNOWN"); 336 break; 337 } 338 value = invalue; 339 340 msyslog(LOG_ERR, 341 "ExpandEnvironmentStrings(%s) failed: %m\n", parameter); 342 } else { 343 value = newvalue; 344 } 345#else 346 value = invalue; 347#endif /* SYS_WINNT */ 348 349 switch(item) { 350 case STATS_FREQ_FILE: 351 if (stats_drift_file != 0) { 352 (void) free(stats_drift_file); 353 (void) free(stats_temp_file); 354 stats_drift_file = 0; 355 stats_temp_file = 0; 356 } 357 358 if (value == 0 || (len = strlen(value)) == 0) 359 break; 360 361 stats_drift_file = (char*)emalloc((u_int)(len + 1)); 362#if !defined(VMS) 363 stats_temp_file = (char*)emalloc((u_int)(len + 364 sizeof(".TEMP"))); 365#else 366 stats_temp_file = (char*)emalloc((u_int)(len + 367 sizeof("-TEMP"))); 368#endif /* VMS */ 369 memmove(stats_drift_file, value, (unsigned)(len+1)); 370 memmove(stats_temp_file, value, (unsigned)len); 371#if !defined(VMS) 372 memmove(stats_temp_file + len, ".TEMP", 373 sizeof(".TEMP")); 374#else 375 memmove(stats_temp_file + len, "-TEMP", 376 sizeof("-TEMP")); 377#endif /* VMS */ 378 379 /* 380 * Open drift file and read frequency. If the file is 381 * missing or contains errors, tell the loop to reset. 382 */ 383 if ((fp = fopen(stats_drift_file, "r")) == NULL) { 384 loop_config(LOOP_DRIFTCOMP, 1e9); 385 break; 386 } 387 if (fscanf(fp, "%lf", &old_drift) != 1) { 388 msyslog(LOG_ERR, "Frequency format error in %s", 389 stats_drift_file); 390 loop_config(LOOP_DRIFTCOMP, 1e9); 391 fclose(fp); 392 break; 393 } 394 fclose(fp); 395 msyslog(LOG_INFO, 396 "frequency initialized %.3f PPM from %s", 397 old_drift, stats_drift_file); 398 loop_config(LOOP_DRIFTCOMP, old_drift / 1e6); 399 break; 400 401 case STATS_STATSDIR: 402 if (strlen(value) >= sizeof(statsdir)) { 403 msyslog(LOG_ERR, 404 "value for statsdir too long (>%d, sigh)", 405 (int)sizeof(statsdir)-1); 406 } else { 407 l_fp now; 408 409 get_systime(&now); 410 strcpy(statsdir,value); 411 if(peerstats.prefix == &statsdir[0] && 412 peerstats.fp != NULL) { 413 fclose(peerstats.fp); 414 peerstats.fp = NULL; 415 filegen_setup(&peerstats, now.l_ui); 416 } 417 if(loopstats.prefix == &statsdir[0] && 418 loopstats.fp != NULL) { 419 fclose(loopstats.fp); 420 loopstats.fp = NULL; 421 filegen_setup(&loopstats, now.l_ui); 422 } 423 if(clockstats.prefix == &statsdir[0] && 424 clockstats.fp != NULL) { 425 fclose(clockstats.fp); 426 clockstats.fp = NULL; 427 filegen_setup(&clockstats, now.l_ui); 428 } 429 if(rawstats.prefix == &statsdir[0] && 430 rawstats.fp != NULL) { 431 fclose(rawstats.fp); 432 rawstats.fp = NULL; 433 filegen_setup(&rawstats, now.l_ui); 434 } 435 if(sysstats.prefix == &statsdir[0] && 436 sysstats.fp != NULL) { 437 fclose(sysstats.fp); 438 sysstats.fp = NULL; 439 filegen_setup(&sysstats, now.l_ui); 440 } 441#ifdef OPENSSL 442 if(cryptostats.prefix == &statsdir[0] && 443 cryptostats.fp != NULL) { 444 fclose(cryptostats.fp); 445 cryptostats.fp = NULL; 446 filegen_setup(&cryptostats, now.l_ui); 447 } 448#endif /* OPENSSL */ 449 } 450 break; 451 452 case STATS_PID_FILE: 453 if ((fp = fopen(value, "w")) == NULL) { 454 msyslog(LOG_ERR, "Can't open %s: %m", value); 455 break; 456 } 457 fprintf(fp, "%d", (int) getpid()); 458 fclose(fp);; 459 break; 460 461 default: 462 /* oh well */ 463 break; 464 } 465} 466 467/* 468 * record_peer_stats - write peer statistics to file 469 * 470 * file format: 471 * day (mjd) 472 * time (s past UTC midnight) 473 * peer (ip address) 474 * peer status word (hex) 475 * peer offset (s) 476 * peer delay (s) 477 * peer error bound (s) 478 * peer error (s) 479*/ 480void 481record_peer_stats( 482 struct sockaddr_storage *addr, 483 int status, 484 double offset, 485 double delay, 486 double dispersion, 487 double skew 488 ) 489{ 490 l_fp now; 491 u_long day; 492 493 if (!stats_control) 494 return; 495 496 get_systime(&now); 497 filegen_setup(&peerstats, now.l_ui); 498 day = now.l_ui / 86400 + MJD_1900; 499 now.l_ui %= 86400; 500 if (peerstats.fp != NULL) { 501 fprintf(peerstats.fp, 502 "%lu %s %s %x %.9f %.9f %.9f %.9f\n", 503 day, ulfptoa(&now, 3), stoa(addr), status, offset, 504 delay, dispersion, skew); 505 fflush(peerstats.fp); 506 } 507} 508/* 509 * record_loop_stats - write loop filter statistics to file 510 * 511 * file format: 512 * day (mjd) 513 * time (s past midnight) 514 * offset (s) 515 * frequency (approx ppm) 516 * time constant (log base 2) 517 */ 518void 519record_loop_stats( 520 double offset, 521 double freq, 522 double jitter, 523 double stability, 524 int spoll 525 ) 526{ 527 l_fp now; 528 u_long day; 529 530 if (!stats_control) 531 return; 532 533 get_systime(&now); 534 filegen_setup(&loopstats, now.l_ui); 535 day = now.l_ui / 86400 + MJD_1900; 536 now.l_ui %= 86400; 537 if (loopstats.fp != NULL) { 538 fprintf(loopstats.fp, "%lu %s %.9f %.6f %.9f %.6f %d\n", 539 day, ulfptoa(&now, 3), offset, freq * 1e6, jitter, 540 stability * 1e6, spoll); 541 fflush(loopstats.fp); 542 } 543} 544 545/* 546 * record_clock_stats - write clock statistics to file 547 * 548 * file format: 549 * day (mjd) 550 * time (s past midnight) 551 * peer (ip address) 552 * text message 553 */ 554void 555record_clock_stats( 556 struct sockaddr_storage *addr, 557 const char *text 558 ) 559{ 560 l_fp now; 561 u_long day; 562 563 if (!stats_control) 564 return; 565 566 get_systime(&now); 567 filegen_setup(&clockstats, now.l_ui); 568 day = now.l_ui / 86400 + MJD_1900; 569 now.l_ui %= 86400; 570 if (clockstats.fp != NULL) { 571 fprintf(clockstats.fp, "%lu %s %s %s\n", 572 day, ulfptoa(&now, 3), stoa(addr), text); 573 fflush(clockstats.fp); 574 } 575} 576 577/* 578 * record_raw_stats - write raw timestamps to file 579 * 580 * 581 * file format 582 * time (s past midnight) 583 * peer ip address 584 * local ip address 585 * t1 t2 t3 t4 timestamps 586 */ 587void 588record_raw_stats( 589 struct sockaddr_storage *srcadr, 590 struct sockaddr_storage *dstadr, 591 l_fp *t1, 592 l_fp *t2, 593 l_fp *t3, 594 l_fp *t4 595 ) 596{ 597 l_fp now; 598 u_long day; 599 600 if (!stats_control) 601 return; 602 603 get_systime(&now); 604 filegen_setup(&rawstats, now.l_ui); 605 day = now.l_ui / 86400 + MJD_1900; 606 now.l_ui %= 86400; 607 if (rawstats.fp != NULL) { 608 fprintf(rawstats.fp, "%lu %s %s %s %s %s %s %s\n", 609 day, ulfptoa(&now, 3), stoa(srcadr), stoa(dstadr), 610 ulfptoa(t1, 9), ulfptoa(t2, 9), ulfptoa(t3, 9), 611 ulfptoa(t4, 9)); 612 fflush(rawstats.fp); 613 } 614} 615 616 617/* 618 * record_sys_stats - write system statistics to file 619 * 620 * file format 621 * time (s past midnight) 622 * time since startup (hr) 623 * packets recieved 624 * packets processed 625 * current version 626 * previous version 627 * bad version 628 * access denied 629 * bad length or format 630 * bad authentication 631 * rate exceeded 632 */ 633void 634record_sys_stats(void) 635{ 636 l_fp now; 637 u_long day; 638 639 if (!stats_control) 640 return; 641 642 get_systime(&now); 643 filegen_setup(&sysstats, now.l_ui); 644 day = now.l_ui / 86400 + MJD_1900; 645 now.l_ui %= 86400; 646 if (sysstats.fp != NULL) { 647 fprintf(sysstats.fp, 648 "%lu %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n", 649 day, ulfptoa(&now, 3), sys_stattime / 3600, 650 sys_received, sys_processed, sys_newversionpkt, 651 sys_oldversionpkt, sys_unknownversion, 652 sys_restricted, sys_badlength, sys_badauth, 653 sys_limitrejected); 654 fflush(sysstats.fp); 655 proto_clr_stats(); 656 } 657} 658 659 660#ifdef OPENSSL 661/* 662 * record_crypto_stats - write crypto statistics to file 663 * 664 * file format: 665 * day (mjd) 666 * time (s past midnight) 667 * peer (ip address) 668 * text message 669 */ 670void 671record_crypto_stats( 672 struct sockaddr_storage *addr, 673 const char *text 674 ) 675{ 676 l_fp now; 677 u_long day; 678 679 if (!stats_control) 680 return; 681 682 get_systime(&now); 683 filegen_setup(&cryptostats, now.l_ui); 684 day = now.l_ui / 86400 + MJD_1900; 685 now.l_ui %= 86400; 686 if (cryptostats.fp != NULL) { 687 if (addr == NULL) 688 fprintf(cryptostats.fp, "%lu %s %s\n", 689 day, ulfptoa(&now, 3), text); 690 else 691 fprintf(cryptostats.fp, "%lu %s %s %s\n", 692 day, ulfptoa(&now, 3), stoa(addr), text); 693 fflush(cryptostats.fp); 694 } 695} 696#endif /* OPENSSL */ 697 698 699/* 700 * getauthkeys - read the authentication keys from the specified file 701 */ 702void 703getauthkeys( 704 char *keyfile 705 ) 706{ 707 int len; 708 709 len = strlen(keyfile); 710 if (len == 0) 711 return; 712 713 if (key_file_name != 0) { 714 if (len > (int)strlen(key_file_name)) { 715 (void) free(key_file_name); 716 key_file_name = 0; 717 } 718 } 719 720 if (key_file_name == 0) { 721#ifndef SYS_WINNT 722 key_file_name = (char*)emalloc((u_int) (len + 1)); 723#else 724 key_file_name = (char*)emalloc((u_int) (MAXPATHLEN)); 725#endif 726 } 727#ifndef SYS_WINNT 728 memmove(key_file_name, keyfile, (unsigned)(len+1)); 729#else 730 if (!ExpandEnvironmentStrings(keyfile, key_file_name, MAXPATHLEN)) 731 { 732 msyslog(LOG_ERR, 733 "ExpandEnvironmentStrings(KEY_FILE) failed: %m\n"); 734 } 735#endif /* SYS_WINNT */ 736 737 authreadkeys(key_file_name); 738} 739 740 741/* 742 * rereadkeys - read the authentication key file over again. 743 */ 744void 745rereadkeys(void) 746{ 747 if (key_file_name != 0) 748 authreadkeys(key_file_name); 749} 750 751/* 752 * sock_hash - hash an sockaddr_storage structure 753 */ 754int 755sock_hash( 756 struct sockaddr_storage *addr 757 ) 758{ 759 int hashVal; 760 int i; 761 int len; 762 char *ch; 763 764 hashVal = 0; 765 len = 0; 766 /* 767 * We can't just hash the whole thing because there are hidden 768 * fields in sockaddr_in6 that might be filled in by recvfrom(), 769 * so just use the family, port and address. 770 */ 771 ch = (char *)&addr->ss_family; 772 hashVal = 37 * hashVal + (int)*ch; 773 if (sizeof(addr->ss_family) > 1) { 774 ch++; 775 hashVal = 37 * hashVal + (int)*ch; 776 } 777 switch(addr->ss_family) { 778 case AF_INET: 779 ch = (char *)&((struct sockaddr_in *)addr)->sin_addr; 780 len = sizeof(struct in_addr); 781 break; 782 case AF_INET6: 783 ch = (char *)&((struct sockaddr_in6 *)addr)->sin6_addr; 784 len = sizeof(struct in6_addr); 785 break; 786 } 787 788 for (i = 0; i < len ; i++) 789 hashVal = 37 * hashVal + (int)*(ch + i); 790 791 hashVal = hashVal % 128; /* % MON_HASH_SIZE hardcoded */ 792 793 if (hashVal < 0) 794 hashVal += 128; 795 796 return hashVal; 797} 798