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