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