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.42 (Berkeley) 07/23/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 (SevenBitInput) 892 { 893 for (p = buf; *p != '\0'; p++) 894 *p &= ~0200; 895 } 896 else if (!HasEightBits) 897 { 898 for (p = buf; *p != '\0'; p++) 899 { 900 if (bitset(0200, *p)) 901 { 902 HasEightBits = TRUE; 903 break; 904 } 905 } 906 } 907 return (buf); 908 } 909 910 void 911 sfgetset(timeout) 912 time_t timeout; 913 { 914 /* cancel pending timer */ 915 if (GlobalTimeout != NULL) 916 { 917 clrevent(GlobalTimeout); 918 GlobalTimeout = NULL; 919 } 920 921 /* schedule fresh one if so requested */ 922 if (timeout != 0) 923 { 924 ReadProgress = LineNumber; 925 GlobalTimeout = setevent(timeout, readtimeout, timeout); 926 } 927 } 928 929 static 930 readtimeout(timeout) 931 time_t timeout; 932 { 933 /* terminate if ordinary timeout */ 934 if (GlobalTimeout == NULL) 935 longjmp(CtxReadTimeout, 1); 936 937 /* terminate if no progress was made -- reset state */ 938 if (EnableTimeout && (LineNumber <= ReadProgress)) 939 { 940 EnableTimeout = FALSE; 941 GlobalTimeout = NULL; 942 longjmp(CtxReadTimeout, 2); 943 } 944 945 /* schedule a new timeout */ 946 GlobalTimeout = NULL; 947 sfgetset(timeout); 948 } 949 /* 950 ** FGETFOLDED -- like fgets, but know about folded lines. 951 ** 952 ** Parameters: 953 ** buf -- place to put result. 954 ** n -- bytes available. 955 ** f -- file to read from. 956 ** 957 ** Returns: 958 ** input line(s) on success, NULL on error or EOF. 959 ** This will normally be buf -- unless the line is too 960 ** long, when it will be xalloc()ed. 961 ** 962 ** Side Effects: 963 ** buf gets lines from f, with continuation lines (lines 964 ** with leading white space) appended. CRLF's are mapped 965 ** into single newlines. Any trailing NL is stripped. 966 */ 967 968 char * 969 fgetfolded(buf, n, f) 970 char *buf; 971 register int n; 972 FILE *f; 973 { 974 register char *p = buf; 975 char *bp = buf; 976 register int i; 977 978 n--; 979 while ((i = getc(f)) != EOF) 980 { 981 if (i == '\r') 982 { 983 i = getc(f); 984 if (i != '\n') 985 { 986 if (i != EOF) 987 (void) ungetc(i, f); 988 i = '\r'; 989 } 990 } 991 if (--n <= 0) 992 { 993 /* allocate new space */ 994 char *nbp; 995 int nn; 996 997 nn = (p - bp); 998 if (nn < MEMCHUNKSIZE) 999 nn *= 2; 1000 else 1001 nn += MEMCHUNKSIZE; 1002 nbp = xalloc(nn); 1003 bcopy(bp, nbp, p - bp); 1004 p = &nbp[p - bp]; 1005 if (bp != buf) 1006 free(bp); 1007 bp = nbp; 1008 n = nn - (p - bp); 1009 } 1010 *p++ = i; 1011 if (i == '\n') 1012 { 1013 LineNumber++; 1014 i = getc(f); 1015 if (i != EOF) 1016 (void) ungetc(i, f); 1017 if (i != ' ' && i != '\t') 1018 break; 1019 } 1020 } 1021 if (p == bp) 1022 return (NULL); 1023 *--p = '\0'; 1024 return (bp); 1025 } 1026 /* 1027 ** CURTIME -- return current time. 1028 ** 1029 ** Parameters: 1030 ** none. 1031 ** 1032 ** Returns: 1033 ** the current time. 1034 ** 1035 ** Side Effects: 1036 ** none. 1037 */ 1038 1039 time_t 1040 curtime() 1041 { 1042 auto time_t t; 1043 1044 (void) time(&t); 1045 return (t); 1046 } 1047 /* 1048 ** ATOBOOL -- convert a string representation to boolean. 1049 ** 1050 ** Defaults to "TRUE" 1051 ** 1052 ** Parameters: 1053 ** s -- string to convert. Takes "tTyY" as true, 1054 ** others as false. 1055 ** 1056 ** Returns: 1057 ** A boolean representation of the string. 1058 ** 1059 ** Side Effects: 1060 ** none. 1061 */ 1062 1063 bool 1064 atobool(s) 1065 register char *s; 1066 { 1067 if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL) 1068 return (TRUE); 1069 return (FALSE); 1070 } 1071 /* 1072 ** ATOOCT -- convert a string representation to octal. 1073 ** 1074 ** Parameters: 1075 ** s -- string to convert. 1076 ** 1077 ** Returns: 1078 ** An integer representing the string interpreted as an 1079 ** octal number. 1080 ** 1081 ** Side Effects: 1082 ** none. 1083 */ 1084 1085 atooct(s) 1086 register char *s; 1087 { 1088 register int i = 0; 1089 1090 while (*s >= '0' && *s <= '7') 1091 i = (i << 3) | (*s++ - '0'); 1092 return (i); 1093 } 1094 /* 1095 ** WAITFOR -- wait for a particular process id. 1096 ** 1097 ** Parameters: 1098 ** pid -- process id to wait for. 1099 ** 1100 ** Returns: 1101 ** status of pid. 1102 ** -1 if pid never shows up. 1103 ** 1104 ** Side Effects: 1105 ** none. 1106 */ 1107 1108 int 1109 waitfor(pid) 1110 int pid; 1111 { 1112 #ifdef WAITUNION 1113 union wait st; 1114 #else 1115 auto int st; 1116 #endif 1117 int i; 1118 1119 do 1120 { 1121 errno = 0; 1122 i = wait(&st); 1123 } while ((i >= 0 || errno == EINTR) && i != pid); 1124 if (i < 0) 1125 return -1; 1126 #ifdef WAITUNION 1127 return st.w_status; 1128 #else 1129 return st; 1130 #endif 1131 } 1132 /* 1133 ** BITINTERSECT -- tell if two bitmaps intersect 1134 ** 1135 ** Parameters: 1136 ** a, b -- the bitmaps in question 1137 ** 1138 ** Returns: 1139 ** TRUE if they have a non-null intersection 1140 ** FALSE otherwise 1141 ** 1142 ** Side Effects: 1143 ** none. 1144 */ 1145 1146 bool 1147 bitintersect(a, b) 1148 BITMAP a; 1149 BITMAP b; 1150 { 1151 int i; 1152 1153 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1154 if ((a[i] & b[i]) != 0) 1155 return (TRUE); 1156 return (FALSE); 1157 } 1158 /* 1159 ** BITZEROP -- tell if a bitmap is all zero 1160 ** 1161 ** Parameters: 1162 ** map -- the bit map to check 1163 ** 1164 ** Returns: 1165 ** TRUE if map is all zero. 1166 ** FALSE if there are any bits set in map. 1167 ** 1168 ** Side Effects: 1169 ** none. 1170 */ 1171 1172 bool 1173 bitzerop(map) 1174 BITMAP map; 1175 { 1176 int i; 1177 1178 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1179 if (map[i] != 0) 1180 return (FALSE); 1181 return (TRUE); 1182 } 1183 /* 1184 ** STRCONTAINEDIN -- tell if one string is contained in another 1185 ** 1186 ** Parameters: 1187 ** a -- possible substring. 1188 ** b -- possible superstring. 1189 ** 1190 ** Returns: 1191 ** TRUE if a is contained in b. 1192 ** FALSE otherwise. 1193 */ 1194 1195 bool 1196 strcontainedin(a, b) 1197 register char *a; 1198 register char *b; 1199 { 1200 int la; 1201 int lb; 1202 int c; 1203 1204 la = strlen(a); 1205 lb = strlen(b); 1206 c = *a; 1207 if (isascii(c) && isupper(c)) 1208 c = tolower(c); 1209 for (; lb-- >= la; b++) 1210 { 1211 if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c) 1212 continue; 1213 if (strncasecmp(a, b, la) == 0) 1214 return TRUE; 1215 } 1216 return FALSE; 1217 } 1218 /* 1219 ** CHECKFD012 -- check low numbered file descriptors 1220 ** 1221 ** File descriptors 0, 1, and 2 should be open at all times. 1222 ** This routine verifies that, and fixes it if not true. 1223 ** 1224 ** Parameters: 1225 ** where -- a tag printed if the assertion failed 1226 ** 1227 ** Returns: 1228 ** none 1229 */ 1230 1231 checkfd012(where) 1232 char *where; 1233 { 1234 #ifdef XDEBUG 1235 register int i; 1236 struct stat stbuf; 1237 1238 for (i = 0; i < 3; i++) 1239 { 1240 if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP) 1241 { 1242 /* oops.... */ 1243 int fd; 1244 1245 syserr("%s: fd %d not open", where, i); 1246 fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666); 1247 if (fd != i) 1248 { 1249 (void) dup2(fd, i); 1250 (void) close(fd); 1251 } 1252 } 1253 } 1254 #endif /* XDEBUG */ 1255 } 1256 /* 1257 ** PRINTOPENFDS -- print the open file descriptors (for debugging) 1258 ** 1259 ** Parameters: 1260 ** logit -- if set, send output to syslog; otherwise 1261 ** print for debugging. 1262 ** 1263 ** Returns: 1264 ** none. 1265 */ 1266 1267 #include <netdb.h> 1268 #include <arpa/inet.h> 1269 1270 printopenfds(logit) 1271 bool logit; 1272 { 1273 register int fd; 1274 extern int DtableSize; 1275 1276 for (fd = 0; fd < DtableSize; fd++) 1277 dumpfd(fd, FALSE, logit); 1278 } 1279 /* 1280 ** DUMPFD -- dump a file descriptor 1281 ** 1282 ** Parameters: 1283 ** fd -- the file descriptor to dump. 1284 ** printclosed -- if set, print a notification even if 1285 ** it is closed; otherwise print nothing. 1286 ** logit -- if set, send output to syslog instead of stdout. 1287 */ 1288 1289 dumpfd(fd, printclosed, logit) 1290 int fd; 1291 bool printclosed; 1292 bool logit; 1293 { 1294 register struct hostent *hp; 1295 register char *p; 1296 char *fmtstr; 1297 struct sockaddr_in sin; 1298 auto int slen; 1299 struct stat st; 1300 char buf[200]; 1301 1302 p = buf; 1303 sprintf(p, "%3d: ", fd); 1304 p += strlen(p); 1305 1306 if (fstat(fd, &st) < 0) 1307 { 1308 if (printclosed || errno != EBADF) 1309 { 1310 sprintf(p, "CANNOT STAT (%s)", errstring(errno)); 1311 goto printit; 1312 } 1313 return; 1314 } 1315 1316 slen = fcntl(fd, F_GETFL, NULL); 1317 if (slen != -1) 1318 { 1319 sprintf(p, "fl=0x%x, ", slen); 1320 p += strlen(p); 1321 } 1322 1323 sprintf(p, "mode=%o: ", st.st_mode); 1324 p += strlen(p); 1325 switch (st.st_mode & S_IFMT) 1326 { 1327 #ifdef S_IFSOCK 1328 case S_IFSOCK: 1329 sprintf(p, "SOCK "); 1330 p += strlen(p); 1331 slen = sizeof sin; 1332 if (getsockname(fd, (struct sockaddr *) &sin, &slen) < 0) 1333 sprintf(p, "(badsock)"); 1334 else 1335 { 1336 hp = gethostbyaddr((char *) &sin.sin_addr, 1337 INADDRSZ, AF_INET); 1338 sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr) 1339 : hp->h_name, ntohs(sin.sin_port)); 1340 } 1341 p += strlen(p); 1342 sprintf(p, "->"); 1343 p += strlen(p); 1344 slen = sizeof sin; 1345 if (getpeername(fd, (struct sockaddr *) &sin, &slen) < 0) 1346 sprintf(p, "(badsock)"); 1347 else 1348 { 1349 hp = gethostbyaddr((char *) &sin.sin_addr, 1350 INADDRSZ, AF_INET); 1351 sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr) 1352 : hp->h_name, ntohs(sin.sin_port)); 1353 } 1354 break; 1355 #endif 1356 1357 case S_IFCHR: 1358 sprintf(p, "CHR: "); 1359 p += strlen(p); 1360 goto defprint; 1361 1362 case S_IFBLK: 1363 sprintf(p, "BLK: "); 1364 p += strlen(p); 1365 goto defprint; 1366 1367 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) 1368 case S_IFIFO: 1369 sprintf(p, "FIFO: "); 1370 p += strlen(p); 1371 goto defprint; 1372 #endif 1373 1374 #ifdef S_IFDIR 1375 case S_IFDIR: 1376 sprintf(p, "DIR: "); 1377 p += strlen(p); 1378 goto defprint; 1379 #endif 1380 1381 #ifdef S_IFLNK 1382 case S_IFLNK: 1383 sprintf(p, "LNK: "); 1384 p += strlen(p); 1385 goto defprint; 1386 #endif 1387 1388 default: 1389 defprint: 1390 if (sizeof st.st_size > sizeof (long)) 1391 fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%qd"; 1392 else 1393 fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld"; 1394 sprintf(p, fmtstr, 1395 major(st.st_dev), minor(st.st_dev), st.st_ino, 1396 st.st_nlink, st.st_uid, st.st_gid, st.st_size); 1397 break; 1398 } 1399 1400 printit: 1401 #ifdef LOG 1402 if (logit) 1403 syslog(LOG_DEBUG, "%s", buf); 1404 else 1405 #endif 1406 printf("%s\n", buf); 1407 } 1408 /* 1409 ** SHORTENSTRING -- return short version of a string 1410 ** 1411 ** If the string is already short, just return it. If it is too 1412 ** long, return the head and tail of the string. 1413 ** 1414 ** Parameters: 1415 ** s -- the string to shorten. 1416 ** m -- the max length of the string. 1417 ** 1418 ** Returns: 1419 ** Either s or a short version of s. 1420 */ 1421 1422 #ifndef MAXSHORTSTR 1423 # define MAXSHORTSTR 203 1424 #endif 1425 1426 char * 1427 shortenstring(s, m) 1428 register char *s; 1429 int m; 1430 { 1431 int l; 1432 static char buf[MAXSHORTSTR + 1]; 1433 1434 l = strlen(s); 1435 if (l < m) 1436 return s; 1437 if (m > MAXSHORTSTR) 1438 m = MAXSHORTSTR; 1439 else if (m < 10) 1440 { 1441 if (m < 5) 1442 { 1443 strncpy(buf, s, m); 1444 buf[m] = '\0'; 1445 return buf; 1446 } 1447 strncpy(buf, s, m - 3); 1448 strcpy(buf + m - 3, "..."); 1449 return buf; 1450 } 1451 m = (m - 3) / 2; 1452 strncpy(buf, s, m); 1453 strcpy(buf + m, "..."); 1454 strcpy(buf + m + 3, s + l - m); 1455 return buf; 1456 } 1457