1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 static char sccsid[] = "@(#)util.c 8.39 (Berkeley) 04/14/94"; 11 #endif /* not lint */ 12 13 # include "sendmail.h" 14 # include <sysexits.h> 15 /* 16 ** STRIPQUOTES -- Strip quotes & quote bits from a string. 17 ** 18 ** Runs through a string and strips off unquoted quote 19 ** characters and quote bits. This is done in place. 20 ** 21 ** Parameters: 22 ** s -- the string to strip. 23 ** 24 ** Returns: 25 ** none. 26 ** 27 ** Side Effects: 28 ** none. 29 ** 30 ** Called By: 31 ** deliver 32 */ 33 34 stripquotes(s) 35 char *s; 36 { 37 register char *p; 38 register char *q; 39 register char c; 40 41 if (s == NULL) 42 return; 43 44 p = q = s; 45 do 46 { 47 c = *p++; 48 if (c == '\\') 49 c = *p++; 50 else if (c == '"') 51 continue; 52 *q++ = c; 53 } while (c != '\0'); 54 } 55 /* 56 ** XALLOC -- Allocate memory and bitch wildly on failure. 57 ** 58 ** THIS IS A CLUDGE. This should be made to give a proper 59 ** error -- but after all, what can we do? 60 ** 61 ** Parameters: 62 ** sz -- size of area to allocate. 63 ** 64 ** Returns: 65 ** pointer to data region. 66 ** 67 ** Side Effects: 68 ** Memory is allocated. 69 */ 70 71 char * 72 xalloc(sz) 73 register int sz; 74 { 75 register char *p; 76 77 /* some systems can't handle size zero mallocs */ 78 if (sz <= 0) 79 sz = 1; 80 81 p = malloc((unsigned) sz); 82 if (p == NULL) 83 { 84 syserr("Out of memory!!"); 85 abort(); 86 /* exit(EX_UNAVAILABLE); */ 87 } 88 return (p); 89 } 90 /* 91 ** COPYPLIST -- copy list of pointers. 92 ** 93 ** This routine is the equivalent of newstr for lists of 94 ** pointers. 95 ** 96 ** Parameters: 97 ** list -- list of pointers to copy. 98 ** Must be NULL terminated. 99 ** copycont -- if TRUE, copy the contents of the vector 100 ** (which must be a string) also. 101 ** 102 ** Returns: 103 ** a copy of 'list'. 104 ** 105 ** Side Effects: 106 ** none. 107 */ 108 109 char ** 110 copyplist(list, copycont) 111 char **list; 112 bool copycont; 113 { 114 register char **vp; 115 register char **newvp; 116 117 for (vp = list; *vp != NULL; vp++) 118 continue; 119 120 vp++; 121 122 newvp = (char **) xalloc((int) (vp - list) * sizeof *vp); 123 bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp); 124 125 if (copycont) 126 { 127 for (vp = newvp; *vp != NULL; vp++) 128 *vp = newstr(*vp); 129 } 130 131 return (newvp); 132 } 133 /* 134 ** COPYQUEUE -- copy address queue. 135 ** 136 ** This routine is the equivalent of newstr for address queues 137 ** addresses marked with QDONTSEND aren't copied 138 ** 139 ** Parameters: 140 ** addr -- list of address structures to copy. 141 ** 142 ** Returns: 143 ** a copy of 'addr'. 144 ** 145 ** Side Effects: 146 ** none. 147 */ 148 149 ADDRESS * 150 copyqueue(addr) 151 ADDRESS *addr; 152 { 153 register ADDRESS *newaddr; 154 ADDRESS *ret; 155 register ADDRESS **tail = &ret; 156 157 while (addr != NULL) 158 { 159 if (!bitset(QDONTSEND, addr->q_flags)) 160 { 161 newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS)); 162 STRUCTCOPY(*addr, *newaddr); 163 *tail = newaddr; 164 tail = &newaddr->q_next; 165 } 166 addr = addr->q_next; 167 } 168 *tail = NULL; 169 170 return ret; 171 } 172 /* 173 ** PRINTAV -- print argument vector. 174 ** 175 ** Parameters: 176 ** av -- argument vector. 177 ** 178 ** Returns: 179 ** none. 180 ** 181 ** Side Effects: 182 ** prints av. 183 */ 184 185 printav(av) 186 register char **av; 187 { 188 while (*av != NULL) 189 { 190 if (tTd(0, 44)) 191 printf("\n\t%08x=", *av); 192 else 193 (void) putchar(' '); 194 xputs(*av++); 195 } 196 (void) putchar('\n'); 197 } 198 /* 199 ** LOWER -- turn letter into lower case. 200 ** 201 ** Parameters: 202 ** c -- character to turn into lower case. 203 ** 204 ** Returns: 205 ** c, in lower case. 206 ** 207 ** Side Effects: 208 ** none. 209 */ 210 211 char 212 lower(c) 213 register char c; 214 { 215 return((isascii(c) && isupper(c)) ? tolower(c) : c); 216 } 217 /* 218 ** XPUTS -- put string doing control escapes. 219 ** 220 ** Parameters: 221 ** s -- string to put. 222 ** 223 ** Returns: 224 ** none. 225 ** 226 ** Side Effects: 227 ** output to stdout 228 */ 229 230 xputs(s) 231 register char *s; 232 { 233 register int c; 234 register struct metamac *mp; 235 extern struct metamac MetaMacros[]; 236 237 if (s == NULL) 238 { 239 printf("<null>"); 240 return; 241 } 242 while ((c = (*s++ & 0377)) != '\0') 243 { 244 if (!isascii(c)) 245 { 246 if (c == MATCHREPL || c == MACROEXPAND) 247 { 248 putchar('$'); 249 continue; 250 } 251 for (mp = MetaMacros; mp->metaname != '\0'; mp++) 252 { 253 if ((mp->metaval & 0377) == c) 254 { 255 printf("$%c", mp->metaname); 256 break; 257 } 258 } 259 if (mp->metaname != '\0') 260 continue; 261 (void) putchar('\\'); 262 c &= 0177; 263 } 264 if (isprint(c)) 265 { 266 putchar(c); 267 continue; 268 } 269 270 /* wasn't a meta-macro -- find another way to print it */ 271 switch (c) 272 { 273 case '\0': 274 continue; 275 276 case '\n': 277 c = 'n'; 278 break; 279 280 case '\r': 281 c = 'r'; 282 break; 283 284 case '\t': 285 c = 't'; 286 break; 287 288 default: 289 (void) putchar('^'); 290 (void) putchar(c ^ 0100); 291 continue; 292 } 293 } 294 (void) fflush(stdout); 295 } 296 /* 297 ** MAKELOWER -- Translate a line into lower case 298 ** 299 ** Parameters: 300 ** p -- the string to translate. If NULL, return is 301 ** immediate. 302 ** 303 ** Returns: 304 ** none. 305 ** 306 ** Side Effects: 307 ** String pointed to by p is translated to lower case. 308 ** 309 ** Called By: 310 ** parse 311 */ 312 313 makelower(p) 314 register char *p; 315 { 316 register char c; 317 318 if (p == NULL) 319 return; 320 for (; (c = *p) != '\0'; p++) 321 if (isascii(c) && isupper(c)) 322 *p = tolower(c); 323 } 324 /* 325 ** BUILDFNAME -- build full name from gecos style entry. 326 ** 327 ** This routine interprets the strange entry that would appear 328 ** in the GECOS field of the password file. 329 ** 330 ** Parameters: 331 ** p -- name to build. 332 ** login -- the login name of this user (for &). 333 ** buf -- place to put the result. 334 ** 335 ** Returns: 336 ** none. 337 ** 338 ** Side Effects: 339 ** none. 340 */ 341 342 buildfname(gecos, login, buf) 343 register char *gecos; 344 char *login; 345 char *buf; 346 { 347 register char *p; 348 register char *bp = buf; 349 int l; 350 351 if (*gecos == '*') 352 gecos++; 353 354 /* find length of final string */ 355 l = 0; 356 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) 357 { 358 if (*p == '&') 359 l += strlen(login); 360 else 361 l++; 362 } 363 364 /* now fill in buf */ 365 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) 366 { 367 if (*p == '&') 368 { 369 (void) strcpy(bp, login); 370 *bp = toupper(*bp); 371 while (*bp != '\0') 372 bp++; 373 } 374 else 375 *bp++ = *p; 376 } 377 *bp = '\0'; 378 } 379 /* 380 ** SAFEFILE -- return true if a file exists and is safe for a user. 381 ** 382 ** Parameters: 383 ** fn -- filename to check. 384 ** uid -- user id to compare against. 385 ** gid -- group id to compare against. 386 ** uname -- user name to compare against (used for group 387 ** sets). 388 ** flags -- modifiers: 389 ** SFF_MUSTOWN -- "uid" must own this file. 390 ** SFF_NOSLINK -- file cannot be a symbolic link. 391 ** mode -- mode bits that must match. 392 ** 393 ** Returns: 394 ** 0 if fn exists, is owned by uid, and matches mode. 395 ** An errno otherwise. The actual errno is cleared. 396 ** 397 ** Side Effects: 398 ** none. 399 */ 400 401 #include <grp.h> 402 403 #ifndef S_IXOTH 404 # define S_IXOTH (S_IEXEC >> 6) 405 #endif 406 407 #ifndef S_IXGRP 408 # define S_IXGRP (S_IEXEC >> 3) 409 #endif 410 411 #ifndef S_IXUSR 412 # define S_IXUSR (S_IEXEC) 413 #endif 414 415 int 416 safefile(fn, uid, gid, uname, flags, mode) 417 char *fn; 418 uid_t uid; 419 gid_t gid; 420 char *uname; 421 int flags; 422 int mode; 423 { 424 register char *p; 425 register struct group *gr = NULL; 426 struct stat stbuf; 427 428 if (tTd(54, 4)) 429 printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n", 430 fn, uid, gid, flags, mode); 431 errno = 0; 432 433 for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/') 434 { 435 *p = '\0'; 436 if (stat(fn, &stbuf) < 0) 437 break; 438 if (uid == 0 && !bitset(SFF_ROOTOK, flags)) 439 { 440 if (bitset(S_IXOTH, stbuf.st_mode)) 441 continue; 442 break; 443 } 444 if (stbuf.st_uid == uid && bitset(S_IXUSR, stbuf.st_mode)) 445 continue; 446 if (stbuf.st_gid == gid && bitset(S_IXGRP, stbuf.st_mode)) 447 continue; 448 #ifndef NO_GROUP_SET 449 if (uname != NULL && 450 ((gr != NULL && gr->gr_gid == stbuf.st_gid) || 451 (gr = getgrgid(stbuf.st_gid)) != NULL)) 452 { 453 register char **gp; 454 455 for (gp = gr->gr_mem; *gp != NULL; gp++) 456 if (strcmp(*gp, uname) == 0) 457 break; 458 if (*gp != NULL && bitset(S_IXGRP, stbuf.st_mode)) 459 continue; 460 } 461 #endif 462 if (!bitset(S_IXOTH, stbuf.st_mode)) 463 break; 464 } 465 if (p != NULL) 466 { 467 int ret = errno; 468 469 if (ret == 0) 470 ret = EACCES; 471 if (tTd(54, 4)) 472 printf("\t[dir %s] %s\n", fn, errstring(ret)); 473 *p = '/'; 474 return ret; 475 } 476 477 #ifdef HASLSTAT 478 if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, &stbuf) 479 : stat(fn, &stbuf)) < 0) 480 #else 481 if (stat(fn, &stbuf) < 0) 482 #endif 483 { 484 int ret = errno; 485 486 if (tTd(54, 4)) 487 printf("\t%s\n", errstring(ret)); 488 489 errno = 0; 490 return ret; 491 } 492 493 #ifdef S_ISLNK 494 if (bitset(SFF_NOSLINK, flags) && S_ISLNK(stbuf.st_mode)) 495 { 496 if (tTd(54, 4)) 497 printf("\t[slink mode %o]\tEPERM\n", stbuf.st_mode); 498 return EPERM; 499 } 500 #endif 501 502 if (uid == 0 && !bitset(SFF_ROOTOK, flags)) 503 mode >>= 6; 504 else if (stbuf.st_uid != uid) 505 { 506 mode >>= 3; 507 if (stbuf.st_gid == gid) 508 ; 509 #ifndef NO_GROUP_SET 510 else if (uname != NULL && 511 ((gr != NULL && gr->gr_gid == stbuf.st_gid) || 512 (gr = getgrgid(stbuf.st_gid)) != NULL)) 513 { 514 register char **gp; 515 516 for (gp = gr->gr_mem; *gp != NULL; gp++) 517 if (strcmp(*gp, uname) == 0) 518 break; 519 if (*gp == NULL) 520 mode >>= 3; 521 } 522 #endif 523 else 524 mode >>= 3; 525 } 526 if (tTd(54, 4)) 527 printf("\t[uid %d, stat %o, mode %o] ", 528 stbuf.st_uid, stbuf.st_mode, mode); 529 if ((stbuf.st_uid == uid || stbuf.st_uid == 0 || 530 !bitset(SFF_MUSTOWN, flags)) && 531 (stbuf.st_mode & mode) == mode) 532 { 533 if (tTd(54, 4)) 534 printf("\tOK\n"); 535 return 0; 536 } 537 if (tTd(54, 4)) 538 printf("\tEACCES\n"); 539 return EACCES; 540 } 541 /* 542 ** FIXCRLF -- fix <CR><LF> in line. 543 ** 544 ** Looks for the <CR><LF> combination and turns it into the 545 ** UNIX canonical <NL> character. It only takes one line, 546 ** i.e., it is assumed that the first <NL> found is the end 547 ** of the line. 548 ** 549 ** Parameters: 550 ** line -- the line to fix. 551 ** stripnl -- if true, strip the newline also. 552 ** 553 ** Returns: 554 ** none. 555 ** 556 ** Side Effects: 557 ** line is changed in place. 558 */ 559 560 fixcrlf(line, stripnl) 561 char *line; 562 bool stripnl; 563 { 564 register char *p; 565 566 p = strchr(line, '\n'); 567 if (p == NULL) 568 return; 569 if (p > line && p[-1] == '\r') 570 p--; 571 if (!stripnl) 572 *p++ = '\n'; 573 *p = '\0'; 574 } 575 /* 576 ** DFOPEN -- determined file open 577 ** 578 ** This routine has the semantics of fopen, except that it will 579 ** keep trying a few times to make this happen. The idea is that 580 ** on very loaded systems, we may run out of resources (inodes, 581 ** whatever), so this tries to get around it. 582 */ 583 584 #ifndef O_ACCMODE 585 # define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) 586 #endif 587 588 struct omodes 589 { 590 int mask; 591 int mode; 592 char *farg; 593 } OpenModes[] = 594 { 595 O_ACCMODE, O_RDONLY, "r", 596 O_ACCMODE|O_APPEND, O_WRONLY, "w", 597 O_ACCMODE|O_APPEND, O_WRONLY|O_APPEND, "a", 598 O_TRUNC, 0, "w+", 599 O_APPEND, O_APPEND, "a+", 600 0, 0, "r+", 601 }; 602 603 FILE * 604 dfopen(filename, omode, cmode) 605 char *filename; 606 int omode; 607 int cmode; 608 { 609 register int tries; 610 int fd; 611 register struct omodes *om; 612 struct stat st; 613 614 for (om = OpenModes; om->mask != 0; om++) 615 if ((omode & om->mask) == om->mode) 616 break; 617 618 for (tries = 0; tries < 10; tries++) 619 { 620 sleep((unsigned) (10 * tries)); 621 errno = 0; 622 fd = open(filename, omode, cmode); 623 if (fd >= 0) 624 break; 625 switch (errno) 626 { 627 case ENFILE: /* system file table full */ 628 case EINTR: /* interrupted syscall */ 629 #ifdef ETXTBSY 630 case ETXTBSY: /* Apollo: net file locked */ 631 #endif 632 continue; 633 } 634 break; 635 } 636 if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode)) 637 { 638 int locktype; 639 640 /* lock the file to avoid accidental conflicts */ 641 if ((omode & O_ACCMODE) != O_RDONLY) 642 locktype = LOCK_EX; 643 else 644 locktype = LOCK_SH; 645 (void) lockfile(fd, filename, NULL, locktype); 646 errno = 0; 647 } 648 if (fd < 0) 649 return NULL; 650 else 651 return fdopen(fd, om->farg); 652 } 653 /* 654 ** PUTLINE -- put a line like fputs obeying SMTP conventions 655 ** 656 ** This routine always guarantees outputing a newline (or CRLF, 657 ** as appropriate) at the end of the string. 658 ** 659 ** Parameters: 660 ** l -- line to put. 661 ** mci -- the mailer connection information. 662 ** 663 ** Returns: 664 ** none 665 ** 666 ** Side Effects: 667 ** output of l to fp. 668 */ 669 670 putline(l, mci) 671 register char *l; 672 register MCI *mci; 673 { 674 register char *p; 675 register char svchar; 676 int slop = 0; 677 678 /* strip out 0200 bits -- these can look like TELNET protocol */ 679 if (bitset(MCIF_7BIT, mci->mci_flags)) 680 { 681 for (p = l; (svchar = *p) != '\0'; ++p) 682 if (bitset(0200, svchar)) 683 *p = svchar &~ 0200; 684 } 685 686 do 687 { 688 /* find the end of the line */ 689 p = strchr(l, '\n'); 690 if (p == NULL) 691 p = &l[strlen(l)]; 692 693 if (TrafficLogFile != NULL) 694 fprintf(TrafficLogFile, "%05d >>> ", getpid()); 695 696 /* check for line overflow */ 697 while (mci->mci_mailer->m_linelimit > 0 && 698 (p - l + slop) > mci->mci_mailer->m_linelimit) 699 { 700 register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1]; 701 702 svchar = *q; 703 *q = '\0'; 704 if (l[0] == '.' && slop == 0 && 705 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 706 { 707 (void) putc('.', mci->mci_out); 708 if (TrafficLogFile != NULL) 709 (void) putc('.', TrafficLogFile); 710 } 711 fputs(l, mci->mci_out); 712 (void) putc('!', mci->mci_out); 713 fputs(mci->mci_mailer->m_eol, mci->mci_out); 714 (void) putc(' ', mci->mci_out); 715 if (TrafficLogFile != NULL) 716 fprintf(TrafficLogFile, "%s!\n%05d >>> ", 717 l, getpid()); 718 *q = svchar; 719 l = q; 720 slop = 1; 721 } 722 723 /* output last part */ 724 if (l[0] == '.' && slop == 0 && 725 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 726 { 727 (void) putc('.', mci->mci_out); 728 if (TrafficLogFile != NULL) 729 (void) putc('.', TrafficLogFile); 730 } 731 if (TrafficLogFile != NULL) 732 fprintf(TrafficLogFile, "%.*s\n", p - l, l); 733 for ( ; l < p; ++l) 734 (void) putc(*l, mci->mci_out); 735 fputs(mci->mci_mailer->m_eol, mci->mci_out); 736 if (*l == '\n') 737 ++l; 738 } while (l[0] != '\0'); 739 } 740 /* 741 ** XUNLINK -- unlink a file, doing logging as appropriate. 742 ** 743 ** Parameters: 744 ** f -- name of file to unlink. 745 ** 746 ** Returns: 747 ** none. 748 ** 749 ** Side Effects: 750 ** f is unlinked. 751 */ 752 753 xunlink(f) 754 char *f; 755 { 756 register int i; 757 758 # ifdef LOG 759 if (LogLevel > 98) 760 syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f); 761 # endif /* LOG */ 762 763 i = unlink(f); 764 # ifdef LOG 765 if (i < 0 && LogLevel > 97) 766 syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno); 767 # endif /* LOG */ 768 } 769 /* 770 ** XFCLOSE -- close a file, doing logging as appropriate. 771 ** 772 ** Parameters: 773 ** fp -- file pointer for the file to close 774 ** a, b -- miscellaneous crud to print for debugging 775 ** 776 ** Returns: 777 ** none. 778 ** 779 ** Side Effects: 780 ** fp is closed. 781 */ 782 783 xfclose(fp, a, b) 784 FILE *fp; 785 char *a, *b; 786 { 787 if (tTd(53, 99)) 788 printf("xfclose(%x) %s %s\n", fp, a, b); 789 #ifdef XDEBUG 790 if (fileno(fp) == 1) 791 syserr("xfclose(%s %s): fd = 1", a, b); 792 #endif 793 if (fclose(fp) < 0 && tTd(53, 99)) 794 printf("xfclose FAILURE: %s\n", errstring(errno)); 795 } 796 /* 797 ** SFGETS -- "safe" fgets -- times out and ignores random interrupts. 798 ** 799 ** Parameters: 800 ** buf -- place to put the input line. 801 ** siz -- size of buf. 802 ** fp -- file to read from. 803 ** timeout -- the timeout before error occurs. 804 ** during -- what we are trying to read (for error messages). 805 ** 806 ** Returns: 807 ** NULL on error (including timeout). This will also leave 808 ** buf containing a null string. 809 ** buf otherwise. 810 ** 811 ** Side Effects: 812 ** none. 813 */ 814 815 static jmp_buf CtxReadTimeout; 816 static int readtimeout(); 817 static EVENT *GlobalTimeout = NULL; 818 static bool EnableTimeout = FALSE; 819 static int ReadProgress; 820 821 char * 822 sfgets(buf, siz, fp, timeout, during) 823 char *buf; 824 int siz; 825 FILE *fp; 826 time_t timeout; 827 char *during; 828 { 829 register EVENT *ev = NULL; 830 register char *p; 831 832 if (fp == NULL) 833 { 834 buf[0] = '\0'; 835 return NULL; 836 } 837 838 /* set the timeout */ 839 if (timeout != 0) 840 { 841 if (setjmp(CtxReadTimeout) != 0) 842 { 843 # ifdef LOG 844 syslog(LOG_NOTICE, 845 "timeout waiting for input from %s during %s\n", 846 CurHostName? CurHostName: "local", during); 847 # endif 848 errno = 0; 849 usrerr("451 timeout waiting for input during %s", 850 during); 851 buf[0] = '\0'; 852 #ifdef XDEBUG 853 checkfd012(during); 854 #endif 855 return (NULL); 856 } 857 if (GlobalTimeout == NULL) 858 ev = setevent(timeout, readtimeout, 0); 859 else 860 EnableTimeout = TRUE; 861 } 862 863 /* try to read */ 864 p = NULL; 865 while (!feof(fp) && !ferror(fp)) 866 { 867 errno = 0; 868 p = fgets(buf, siz, fp); 869 if (p != NULL || errno != EINTR) 870 break; 871 clearerr(fp); 872 } 873 874 /* clear the event if it has not sprung */ 875 if (GlobalTimeout == NULL) 876 clrevent(ev); 877 else 878 EnableTimeout = FALSE; 879 880 /* clean up the books and exit */ 881 LineNumber++; 882 if (p == NULL) 883 { 884 buf[0] = '\0'; 885 if (TrafficLogFile != NULL) 886 fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid()); 887 return (NULL); 888 } 889 if (TrafficLogFile != NULL) 890 fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf); 891 if (SevenBit) 892 for (p = buf; *p != '\0'; p++) 893 *p &= ~0200; 894 return (buf); 895 } 896 897 void 898 sfgetset(timeout) 899 time_t timeout; 900 { 901 /* cancel pending timer */ 902 if (GlobalTimeout != NULL) 903 { 904 clrevent(GlobalTimeout); 905 GlobalTimeout = NULL; 906 } 907 908 /* schedule fresh one if so requested */ 909 if (timeout != 0) 910 { 911 ReadProgress = LineNumber; 912 GlobalTimeout = setevent(timeout, readtimeout, timeout); 913 } 914 } 915 916 static 917 readtimeout(timeout) 918 time_t timeout; 919 { 920 /* terminate if ordinary timeout */ 921 if (GlobalTimeout == NULL) 922 longjmp(CtxReadTimeout, 1); 923 924 /* terminate if no progress was made -- reset state */ 925 if (EnableTimeout && (LineNumber <= ReadProgress)) 926 { 927 EnableTimeout = FALSE; 928 GlobalTimeout = NULL; 929 longjmp(CtxReadTimeout, 2); 930 } 931 932 /* schedule a new timeout */ 933 GlobalTimeout = NULL; 934 sfgetset(timeout); 935 } 936 /* 937 ** FGETFOLDED -- like fgets, but know about folded lines. 938 ** 939 ** Parameters: 940 ** buf -- place to put result. 941 ** n -- bytes available. 942 ** f -- file to read from. 943 ** 944 ** Returns: 945 ** input line(s) on success, NULL on error or EOF. 946 ** This will normally be buf -- unless the line is too 947 ** long, when it will be xalloc()ed. 948 ** 949 ** Side Effects: 950 ** buf gets lines from f, with continuation lines (lines 951 ** with leading white space) appended. CRLF's are mapped 952 ** into single newlines. Any trailing NL is stripped. 953 */ 954 955 char * 956 fgetfolded(buf, n, f) 957 char *buf; 958 register int n; 959 FILE *f; 960 { 961 register char *p = buf; 962 char *bp = buf; 963 register int i; 964 965 n--; 966 while ((i = getc(f)) != EOF) 967 { 968 if (i == '\r') 969 { 970 i = getc(f); 971 if (i != '\n') 972 { 973 if (i != EOF) 974 (void) ungetc(i, f); 975 i = '\r'; 976 } 977 } 978 if (--n <= 0) 979 { 980 /* allocate new space */ 981 char *nbp; 982 int nn; 983 984 nn = (p - bp); 985 if (nn < MEMCHUNKSIZE) 986 nn *= 2; 987 else 988 nn += MEMCHUNKSIZE; 989 nbp = xalloc(nn); 990 bcopy(bp, nbp, p - bp); 991 p = &nbp[p - bp]; 992 if (bp != buf) 993 free(bp); 994 bp = nbp; 995 n = nn - (p - bp); 996 } 997 *p++ = i; 998 if (i == '\n') 999 { 1000 LineNumber++; 1001 i = getc(f); 1002 if (i != EOF) 1003 (void) ungetc(i, f); 1004 if (i != ' ' && i != '\t') 1005 break; 1006 } 1007 } 1008 if (p == bp) 1009 return (NULL); 1010 *--p = '\0'; 1011 return (bp); 1012 } 1013 /* 1014 ** CURTIME -- return current time. 1015 ** 1016 ** Parameters: 1017 ** none. 1018 ** 1019 ** Returns: 1020 ** the current time. 1021 ** 1022 ** Side Effects: 1023 ** none. 1024 */ 1025 1026 time_t 1027 curtime() 1028 { 1029 auto time_t t; 1030 1031 (void) time(&t); 1032 return (t); 1033 } 1034 /* 1035 ** ATOBOOL -- convert a string representation to boolean. 1036 ** 1037 ** Defaults to "TRUE" 1038 ** 1039 ** Parameters: 1040 ** s -- string to convert. Takes "tTyY" as true, 1041 ** others as false. 1042 ** 1043 ** Returns: 1044 ** A boolean representation of the string. 1045 ** 1046 ** Side Effects: 1047 ** none. 1048 */ 1049 1050 bool 1051 atobool(s) 1052 register char *s; 1053 { 1054 if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL) 1055 return (TRUE); 1056 return (FALSE); 1057 } 1058 /* 1059 ** ATOOCT -- convert a string representation to octal. 1060 ** 1061 ** Parameters: 1062 ** s -- string to convert. 1063 ** 1064 ** Returns: 1065 ** An integer representing the string interpreted as an 1066 ** octal number. 1067 ** 1068 ** Side Effects: 1069 ** none. 1070 */ 1071 1072 atooct(s) 1073 register char *s; 1074 { 1075 register int i = 0; 1076 1077 while (*s >= '0' && *s <= '7') 1078 i = (i << 3) | (*s++ - '0'); 1079 return (i); 1080 } 1081 /* 1082 ** WAITFOR -- wait for a particular process id. 1083 ** 1084 ** Parameters: 1085 ** pid -- process id to wait for. 1086 ** 1087 ** Returns: 1088 ** status of pid. 1089 ** -1 if pid never shows up. 1090 ** 1091 ** Side Effects: 1092 ** none. 1093 */ 1094 1095 int 1096 waitfor(pid) 1097 int pid; 1098 { 1099 #ifdef WAITUNION 1100 union wait st; 1101 #else 1102 auto int st; 1103 #endif 1104 int i; 1105 1106 do 1107 { 1108 errno = 0; 1109 i = wait(&st); 1110 } while ((i >= 0 || errno == EINTR) && i != pid); 1111 if (i < 0) 1112 return -1; 1113 #ifdef WAITUNION 1114 return st.w_status; 1115 #else 1116 return st; 1117 #endif 1118 } 1119 /* 1120 ** BITINTERSECT -- tell if two bitmaps intersect 1121 ** 1122 ** Parameters: 1123 ** a, b -- the bitmaps in question 1124 ** 1125 ** Returns: 1126 ** TRUE if they have a non-null intersection 1127 ** FALSE otherwise 1128 ** 1129 ** Side Effects: 1130 ** none. 1131 */ 1132 1133 bool 1134 bitintersect(a, b) 1135 BITMAP a; 1136 BITMAP b; 1137 { 1138 int i; 1139 1140 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1141 if ((a[i] & b[i]) != 0) 1142 return (TRUE); 1143 return (FALSE); 1144 } 1145 /* 1146 ** BITZEROP -- tell if a bitmap is all zero 1147 ** 1148 ** Parameters: 1149 ** map -- the bit map to check 1150 ** 1151 ** Returns: 1152 ** TRUE if map is all zero. 1153 ** FALSE if there are any bits set in map. 1154 ** 1155 ** Side Effects: 1156 ** none. 1157 */ 1158 1159 bool 1160 bitzerop(map) 1161 BITMAP map; 1162 { 1163 int i; 1164 1165 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1166 if (map[i] != 0) 1167 return (FALSE); 1168 return (TRUE); 1169 } 1170 /* 1171 ** STRCONTAINEDIN -- tell if one string is contained in another 1172 ** 1173 ** Parameters: 1174 ** a -- possible substring. 1175 ** b -- possible superstring. 1176 ** 1177 ** Returns: 1178 ** TRUE if a is contained in b. 1179 ** FALSE otherwise. 1180 */ 1181 1182 bool 1183 strcontainedin(a, b) 1184 register char *a; 1185 register char *b; 1186 { 1187 int la; 1188 int lb; 1189 int c; 1190 1191 la = strlen(a); 1192 lb = strlen(b); 1193 c = *a; 1194 if (isascii(c) && isupper(c)) 1195 c = tolower(c); 1196 for (; lb-- >= la; b++) 1197 { 1198 if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c) 1199 continue; 1200 if (strncasecmp(a, b, la) == 0) 1201 return TRUE; 1202 } 1203 return FALSE; 1204 } 1205 /* 1206 ** CHECKFD012 -- check low numbered file descriptors 1207 ** 1208 ** File descriptors 0, 1, and 2 should be open at all times. 1209 ** This routine verifies that, and fixes it if not true. 1210 ** 1211 ** Parameters: 1212 ** where -- a tag printed if the assertion failed 1213 ** 1214 ** Returns: 1215 ** none 1216 */ 1217 1218 checkfd012(where) 1219 char *where; 1220 { 1221 #ifdef XDEBUG 1222 register int i; 1223 struct stat stbuf; 1224 1225 for (i = 0; i < 3; i++) 1226 { 1227 if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP) 1228 { 1229 /* oops.... */ 1230 int fd; 1231 1232 syserr("%s: fd %d not open", where, i); 1233 fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666); 1234 if (fd != i) 1235 { 1236 (void) dup2(fd, i); 1237 (void) close(fd); 1238 } 1239 } 1240 } 1241 #endif /* XDEBUG */ 1242 } 1243 /* 1244 ** PRINTOPENFDS -- print the open file descriptors (for debugging) 1245 ** 1246 ** Parameters: 1247 ** logit -- if set, send output to syslog; otherwise 1248 ** print for debugging. 1249 ** 1250 ** Returns: 1251 ** none. 1252 */ 1253 1254 #include <netdb.h> 1255 #include <arpa/inet.h> 1256 1257 printopenfds(logit) 1258 bool logit; 1259 { 1260 register int fd; 1261 extern int DtableSize; 1262 1263 for (fd = 0; fd < DtableSize; fd++) 1264 dumpfd(fd, FALSE, logit); 1265 } 1266 /* 1267 ** DUMPFD -- dump a file descriptor 1268 ** 1269 ** Parameters: 1270 ** fd -- the file descriptor to dump. 1271 ** printclosed -- if set, print a notification even if 1272 ** it is closed; otherwise print nothing. 1273 ** logit -- if set, send output to syslog instead of stdout. 1274 */ 1275 1276 dumpfd(fd, printclosed, logit) 1277 int fd; 1278 bool printclosed; 1279 bool logit; 1280 { 1281 register struct hostent *hp; 1282 register char *p; 1283 char *fmtstr; 1284 struct sockaddr_in sin; 1285 auto int slen; 1286 struct stat st; 1287 char buf[200]; 1288 1289 p = buf; 1290 sprintf(p, "%3d: ", fd); 1291 p += strlen(p); 1292 1293 if (fstat(fd, &st) < 0) 1294 { 1295 if (printclosed || errno != EBADF) 1296 { 1297 sprintf(p, "CANNOT STAT (%s)", errstring(errno)); 1298 goto printit; 1299 } 1300 return; 1301 } 1302 1303 slen = fcntl(fd, F_GETFL, NULL); 1304 if (slen != -1) 1305 { 1306 sprintf(p, "fl=0x%x, ", slen); 1307 p += strlen(p); 1308 } 1309 1310 sprintf(p, "mode=%o: ", st.st_mode); 1311 p += strlen(p); 1312 switch (st.st_mode & S_IFMT) 1313 { 1314 #ifdef S_IFSOCK 1315 case S_IFSOCK: 1316 sprintf(p, "SOCK "); 1317 p += strlen(p); 1318 slen = sizeof sin; 1319 if (getsockname(fd, (struct sockaddr *) &sin, &slen) < 0) 1320 sprintf(p, "(badsock)"); 1321 else 1322 { 1323 hp = gethostbyaddr((char *) &sin.sin_addr, slen, AF_INET); 1324 sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr) 1325 : hp->h_name, ntohs(sin.sin_port)); 1326 } 1327 p += strlen(p); 1328 sprintf(p, "->"); 1329 p += strlen(p); 1330 slen = sizeof sin; 1331 if (getpeername(fd, (struct sockaddr *) &sin, &slen) < 0) 1332 sprintf(p, "(badsock)"); 1333 else 1334 { 1335 hp = gethostbyaddr((char *) &sin.sin_addr, slen, AF_INET); 1336 sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr) 1337 : hp->h_name, ntohs(sin.sin_port)); 1338 } 1339 break; 1340 #endif 1341 1342 case S_IFCHR: 1343 sprintf(p, "CHR: "); 1344 p += strlen(p); 1345 goto defprint; 1346 1347 case S_IFBLK: 1348 sprintf(p, "BLK: "); 1349 p += strlen(p); 1350 goto defprint; 1351 1352 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) 1353 case S_IFIFO: 1354 sprintf(p, "FIFO: "); 1355 p += strlen(p); 1356 goto defprint; 1357 #endif 1358 1359 #ifdef S_IFDIR 1360 case S_IFDIR: 1361 sprintf(p, "DIR: "); 1362 p += strlen(p); 1363 goto defprint; 1364 #endif 1365 1366 #ifdef S_IFLNK 1367 case S_IFLNK: 1368 sprintf(p, "LNK: "); 1369 p += strlen(p); 1370 goto defprint; 1371 #endif 1372 1373 default: 1374 defprint: 1375 if (sizeof st.st_size > sizeof (long)) 1376 fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%qd"; 1377 else 1378 fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld"; 1379 sprintf(p, fmtstr, 1380 major(st.st_dev), minor(st.st_dev), st.st_ino, 1381 st.st_nlink, st.st_uid, st.st_gid, st.st_size); 1382 break; 1383 } 1384 1385 printit: 1386 #ifdef LOG 1387 if (logit) 1388 syslog(LOG_DEBUG, "%s", buf); 1389 else 1390 #endif 1391 printf("%s\n", buf); 1392 } 1393 /* 1394 ** SHORTENSTRING -- return short version of a string 1395 ** 1396 ** If the string is already short, just return it. If it is too 1397 ** long, return the head and tail of the string. 1398 ** 1399 ** Parameters: 1400 ** s -- the string to shorten. 1401 ** m -- the max length of the string. 1402 ** 1403 ** Returns: 1404 ** Either s or a short version of s. 1405 */ 1406 1407 #ifndef MAXSHORTSTR 1408 # define MAXSHORTSTR 203 1409 #endif 1410 1411 char * 1412 shortenstring(s, m) 1413 register char *s; 1414 int m; 1415 { 1416 int l; 1417 static char buf[MAXSHORTSTR + 1]; 1418 1419 l = strlen(s); 1420 if (l < m) 1421 return s; 1422 if (m > MAXSHORTSTR) 1423 m = MAXSHORTSTR; 1424 else if (m < 10) 1425 { 1426 if (m < 5) 1427 { 1428 strncpy(buf, s, m); 1429 buf[m] = '\0'; 1430 return buf; 1431 } 1432 strncpy(buf, s, m - 3); 1433 strcpy(buf + m - 3, "..."); 1434 return buf; 1435 } 1436 m = (m - 3) / 2; 1437 strncpy(buf, s, m); 1438 strcpy(buf + m, "..."); 1439 strcpy(buf + m + 3, s + l - m); 1440 return buf; 1441 } 1442