util.c revision 94334
1/* 2 * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 5 * Copyright (c) 1988, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * By using this file, you agree to the terms and conditions set 9 * forth in the LICENSE file which can be found at the top level of 10 * the sendmail distribution. 11 * 12 */ 13 14#include <sendmail.h> 15 16SM_RCSID("@(#)$Id: util.c,v 8.360 2002/04/04 21:32:15 gshapiro Exp $") 17 18#include <sysexits.h> 19#include <sm/xtrap.h> 20 21/* 22** ADDQUOTES -- Adds quotes & quote bits to a string. 23** 24** Runs through a string and adds backslashes and quote bits. 25** 26** Parameters: 27** s -- the string to modify. 28** rpool -- resource pool from which to allocate result 29** 30** Returns: 31** pointer to quoted string. 32*/ 33 34char * 35addquotes(s, rpool) 36 char *s; 37 SM_RPOOL_T *rpool; 38{ 39 int len = 0; 40 char c; 41 char *p = s, *q, *r; 42 43 if (s == NULL) 44 return NULL; 45 46 /* Find length of quoted string */ 47 while ((c = *p++) != '\0') 48 { 49 len++; 50 if (c == '\\' || c == '"') 51 len++; 52 } 53 54 q = r = sm_rpool_malloc_x(rpool, len + 3); 55 p = s; 56 57 /* add leading quote */ 58 *q++ = '"'; 59 while ((c = *p++) != '\0') 60 { 61 /* quote \ or " */ 62 if (c == '\\' || c == '"') 63 *q++ = '\\'; 64 *q++ = c; 65 } 66 *q++ = '"'; 67 *q = '\0'; 68 return r; 69} 70/* 71** RFC822_STRING -- Checks string for proper RFC822 string quoting. 72** 73** Runs through a string and verifies RFC822 special characters 74** are only found inside comments, quoted strings, or backslash 75** escaped. Also verified balanced quotes and parenthesis. 76** 77** Parameters: 78** s -- the string to modify. 79** 80** Returns: 81** true iff the string is RFC822 compliant, false otherwise. 82*/ 83 84bool 85rfc822_string(s) 86 char *s; 87{ 88 bool quoted = false; 89 int commentlev = 0; 90 char *c = s; 91 92 if (s == NULL) 93 return false; 94 95 while (*c != '\0') 96 { 97 /* escaped character */ 98 if (*c == '\\') 99 { 100 c++; 101 if (*c == '\0') 102 return false; 103 } 104 else if (commentlev == 0 && *c == '"') 105 quoted = !quoted; 106 else if (!quoted) 107 { 108 if (*c == ')') 109 { 110 /* unbalanced ')' */ 111 if (commentlev == 0) 112 return false; 113 else 114 commentlev--; 115 } 116 else if (*c == '(') 117 commentlev++; 118 else if (commentlev == 0 && 119 strchr(MustQuoteChars, *c) != NULL) 120 return false; 121 } 122 c++; 123 } 124 125 /* unbalanced '"' or '(' */ 126 return !quoted && commentlev == 0; 127} 128/* 129** SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string 130** 131** Arbitrarily shorten (in place) an RFC822 string and rebalance 132** comments and quotes. 133** 134** Parameters: 135** string -- the string to shorten 136** length -- the maximum size, 0 if no maximum 137** 138** Returns: 139** true if string is changed, false otherwise 140** 141** Side Effects: 142** Changes string in place, possibly resulting 143** in a shorter string. 144*/ 145 146bool 147shorten_rfc822_string(string, length) 148 char *string; 149 size_t length; 150{ 151 bool backslash = false; 152 bool modified = false; 153 bool quoted = false; 154 size_t slen; 155 int parencount = 0; 156 char *ptr = string; 157 158 /* 159 ** If have to rebalance an already short enough string, 160 ** need to do it within allocated space. 161 */ 162 163 slen = strlen(string); 164 if (length == 0 || slen < length) 165 length = slen; 166 167 while (*ptr != '\0') 168 { 169 if (backslash) 170 { 171 backslash = false; 172 goto increment; 173 } 174 175 if (*ptr == '\\') 176 backslash = true; 177 else if (*ptr == '(') 178 { 179 if (!quoted) 180 parencount++; 181 } 182 else if (*ptr == ')') 183 { 184 if (--parencount < 0) 185 parencount = 0; 186 } 187 188 /* Inside a comment, quotes don't matter */ 189 if (parencount <= 0 && *ptr == '"') 190 quoted = !quoted; 191 192increment: 193 /* Check for sufficient space for next character */ 194 if (length - (ptr - string) <= (size_t) ((backslash ? 1 : 0) + 195 parencount + 196 (quoted ? 1 : 0))) 197 { 198 /* Not enough, backtrack */ 199 if (*ptr == '\\') 200 backslash = false; 201 else if (*ptr == '(' && !quoted) 202 parencount--; 203 else if (*ptr == '"' && parencount == 0) 204 quoted = false; 205 break; 206 } 207 ptr++; 208 } 209 210 /* Rebalance */ 211 while (parencount-- > 0) 212 { 213 if (*ptr != ')') 214 { 215 modified = true; 216 *ptr = ')'; 217 } 218 ptr++; 219 } 220 if (quoted) 221 { 222 if (*ptr != '"') 223 { 224 modified = true; 225 *ptr = '"'; 226 } 227 ptr++; 228 } 229 if (*ptr != '\0') 230 { 231 modified = true; 232 *ptr = '\0'; 233 } 234 return modified; 235} 236/* 237** FIND_CHARACTER -- find an unquoted character in an RFC822 string 238** 239** Find an unquoted, non-commented character in an RFC822 240** string and return a pointer to its location in the 241** string. 242** 243** Parameters: 244** string -- the string to search 245** character -- the character to find 246** 247** Returns: 248** pointer to the character, or 249** a pointer to the end of the line if character is not found 250*/ 251 252char * 253find_character(string, character) 254 char *string; 255 int character; 256{ 257 bool backslash = false; 258 bool quoted = false; 259 int parencount = 0; 260 261 while (string != NULL && *string != '\0') 262 { 263 if (backslash) 264 { 265 backslash = false; 266 if (!quoted && character == '\\' && *string == '\\') 267 break; 268 string++; 269 continue; 270 } 271 switch (*string) 272 { 273 case '\\': 274 backslash = true; 275 break; 276 277 case '(': 278 if (!quoted) 279 parencount++; 280 break; 281 282 case ')': 283 if (--parencount < 0) 284 parencount = 0; 285 break; 286 } 287 288 /* Inside a comment, nothing matters */ 289 if (parencount > 0) 290 { 291 string++; 292 continue; 293 } 294 295 if (*string == '"') 296 quoted = !quoted; 297 else if (*string == character && !quoted) 298 break; 299 string++; 300 } 301 302 /* Return pointer to the character */ 303 return string; 304} 305 306/* 307** CHECK_BODYTYPE -- check bodytype parameter 308** 309** Parameters: 310** bodytype -- bodytype parameter 311** 312** Returns: 313** BODYTYPE_* according to parameter 314** 315*/ 316 317int 318check_bodytype(bodytype) 319 char *bodytype; 320{ 321 /* check body type for legality */ 322 if (bodytype == NULL) 323 return BODYTYPE_NONE; 324 if (sm_strcasecmp(bodytype, "7BIT") == 0) 325 return BODYTYPE_7BIT; 326 if (sm_strcasecmp(bodytype, "8BITMIME") == 0) 327 return BODYTYPE_8BITMIME; 328 return BODYTYPE_ILLEGAL; 329} 330 331#if _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI 332/* 333** TRUNCATE_AT_DELIM -- truncate string at a delimiter and append "..." 334** 335** Parameters: 336** str -- string to truncate 337** len -- maximum length (including '\0') (0 for unlimited) 338** delim -- delimiter character 339** 340** Returns: 341** None. 342*/ 343 344void 345truncate_at_delim(str, len, delim) 346 char *str; 347 size_t len; 348 int delim; 349{ 350 char *p; 351 352 if (str == NULL || len == 0 || strlen(str) < len) 353 return; 354 355 *(str + len - 1) = '\0'; 356 while ((p = strrchr(str, delim)) != NULL) 357 { 358 *p = '\0'; 359 if (p - str + 4 < len) 360 { 361 *p++ = ':'; 362 *p = '\0'; 363 (void) sm_strlcat(str, "...", len); 364 return; 365 } 366 } 367 368 /* Couldn't find a place to append "..." */ 369 if (len > 3) 370 (void) sm_strlcpy(str, "...", len); 371 else 372 str[0] = '\0'; 373} 374#endif /* _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI */ 375/* 376** XALLOC -- Allocate memory, raise an exception on error 377** 378** Parameters: 379** sz -- size of area to allocate. 380** 381** Returns: 382** pointer to data region. 383** 384** Exceptions: 385** SmHeapOutOfMemory (F:sm.heap) -- cannot allocate memory 386** 387** Side Effects: 388** Memory is allocated. 389*/ 390 391char * 392#if SM_HEAP_CHECK 393xalloc_tagged(sz, file, line) 394 register int sz; 395 char *file; 396 int line; 397#else /* SM_HEAP_CHECK */ 398xalloc(sz) 399 register int sz; 400#endif /* SM_HEAP_CHECK */ 401{ 402 register char *p; 403 404 /* some systems can't handle size zero mallocs */ 405 if (sz <= 0) 406 sz = 1; 407 408 /* scaffolding for testing error handling code */ 409 sm_xtrap_raise_x(&SmHeapOutOfMemory); 410 411 p = sm_malloc_tagged((unsigned) sz, file, line, sm_heap_group()); 412 if (p == NULL) 413 { 414 sm_exc_raise_x(&SmHeapOutOfMemory); 415 } 416 return p; 417} 418/* 419** COPYPLIST -- copy list of pointers. 420** 421** This routine is the equivalent of strdup for lists of 422** pointers. 423** 424** Parameters: 425** list -- list of pointers to copy. 426** Must be NULL terminated. 427** copycont -- if true, copy the contents of the vector 428** (which must be a string) also. 429** rpool -- resource pool from which to allocate storage, 430** or NULL 431** 432** Returns: 433** a copy of 'list'. 434*/ 435 436char ** 437copyplist(list, copycont, rpool) 438 char **list; 439 bool copycont; 440 SM_RPOOL_T *rpool; 441{ 442 register char **vp; 443 register char **newvp; 444 445 for (vp = list; *vp != NULL; vp++) 446 continue; 447 448 vp++; 449 450 newvp = (char **) sm_rpool_malloc_x(rpool, (vp - list) * sizeof *vp); 451 memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof *vp); 452 453 if (copycont) 454 { 455 for (vp = newvp; *vp != NULL; vp++) 456 *vp = sm_rpool_strdup_x(rpool, *vp); 457 } 458 459 return newvp; 460} 461/* 462** COPYQUEUE -- copy address queue. 463** 464** This routine is the equivalent of strdup for address queues; 465** addresses marked as QS_IS_DEAD() aren't copied 466** 467** Parameters: 468** addr -- list of address structures to copy. 469** rpool -- resource pool from which to allocate storage 470** 471** Returns: 472** a copy of 'addr'. 473*/ 474 475ADDRESS * 476copyqueue(addr, rpool) 477 ADDRESS *addr; 478 SM_RPOOL_T *rpool; 479{ 480 register ADDRESS *newaddr; 481 ADDRESS *ret; 482 register ADDRESS **tail = &ret; 483 484 while (addr != NULL) 485 { 486 if (!QS_IS_DEAD(addr->q_state)) 487 { 488 newaddr = (ADDRESS *) sm_rpool_malloc_x(rpool, 489 sizeof *newaddr); 490 STRUCTCOPY(*addr, *newaddr); 491 *tail = newaddr; 492 tail = &newaddr->q_next; 493 } 494 addr = addr->q_next; 495 } 496 *tail = NULL; 497 498 return ret; 499} 500/* 501** LOG_SENDMAIL_PID -- record sendmail pid and command line. 502** 503** Parameters: 504** e -- the current envelope. 505** 506** Returns: 507** none. 508** 509** Side Effects: 510** writes pidfile, logs command line. 511*/ 512 513void 514log_sendmail_pid(e) 515 ENVELOPE *e; 516{ 517 long sff; 518 SM_FILE_T *pidf; 519 char pidpath[MAXPATHLEN + 1]; 520 extern char *CommandLineArgs; 521 522 /* write the pid to the log file for posterity */ 523 sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT; 524 if (TrustedUid != 0 && RealUid == TrustedUid) 525 sff |= SFF_OPENASROOT; 526 expand(PidFile, pidpath, sizeof pidpath, e); 527 pidf = safefopen(pidpath, O_WRONLY|O_TRUNC, 0644, sff); 528 if (pidf == NULL) 529 { 530 sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s", 531 pidpath, sm_errstring(errno)); 532 } 533 else 534 { 535 pid_t pid; 536 537 pid = getpid(); 538 539 /* write the process id on line 1 */ 540 (void) sm_io_fprintf(pidf, SM_TIME_DEFAULT, "%ld\n", 541 (long) pid); 542 543 /* line 2 contains all command line flags */ 544 (void) sm_io_fprintf(pidf, SM_TIME_DEFAULT, "%s\n", 545 CommandLineArgs); 546 547 /* flush and close */ 548 (void) sm_io_close(pidf, SM_TIME_DEFAULT); 549 } 550 if (LogLevel > 9) 551 sm_syslog(LOG_INFO, NOQID, "started as: %s", CommandLineArgs); 552} 553/* 554** SET_DELIVERY_MODE -- set and record the delivery mode 555** 556** Parameters: 557** mode -- delivery mode 558** e -- the current envelope. 559** 560** Returns: 561** none. 562** 563** Side Effects: 564** sets {deliveryMode} macro 565*/ 566 567void 568set_delivery_mode(mode, e) 569 int mode; 570 ENVELOPE *e; 571{ 572 char buf[2]; 573 574 e->e_sendmode = (char) mode; 575 buf[0] = (char) mode; 576 buf[1] = '\0'; 577 macdefine(&e->e_macro, A_TEMP, macid("{deliveryMode}"), buf); 578} 579/* 580** SET_OP_MODE -- set and record the op mode 581** 582** Parameters: 583** mode -- op mode 584** e -- the current envelope. 585** 586** Returns: 587** none. 588** 589** Side Effects: 590** sets {opMode} macro 591*/ 592 593void 594set_op_mode(mode) 595 int mode; 596{ 597 char buf[2]; 598 extern ENVELOPE BlankEnvelope; 599 600 OpMode = (char) mode; 601 buf[0] = (char) mode; 602 buf[1] = '\0'; 603 macdefine(&BlankEnvelope.e_macro, A_TEMP, MID_OPMODE, buf); 604} 605/* 606** PRINTAV -- print argument vector. 607** 608** Parameters: 609** av -- argument vector. 610** 611** Returns: 612** none. 613** 614** Side Effects: 615** prints av. 616*/ 617 618void 619printav(av) 620 register char **av; 621{ 622 while (*av != NULL) 623 { 624 if (tTd(0, 44)) 625 sm_dprintf("\n\t%08lx=", (unsigned long) *av); 626 else 627 (void) sm_io_putc(smioout, SM_TIME_DEFAULT, ' '); 628 xputs(*av++); 629 } 630 (void) sm_io_putc(smioout, SM_TIME_DEFAULT, '\n'); 631} 632/* 633** XPUTS -- put string doing control escapes. 634** 635** Parameters: 636** s -- string to put. 637** 638** Returns: 639** none. 640** 641** Side Effects: 642** output to stdout 643*/ 644 645void 646xputs(s) 647 register const char *s; 648{ 649 register int c; 650 register struct metamac *mp; 651 bool shiftout = false; 652 extern struct metamac MetaMacros[]; 653 static SM_DEBUG_T DebugANSI = SM_DEBUG_INITIALIZER("ANSI", 654 "@(#)$Debug: ANSI - enable reverse video in debug output $"); 655 656 /* 657 ** TermEscape is set here, rather than in main(), 658 ** because ANSI mode can be turned on or off at any time 659 ** if we are in -bt rule testing mode. 660 */ 661 662 if (sm_debug_unknown(&DebugANSI)) 663 { 664 if (sm_debug_active(&DebugANSI, 1)) 665 { 666 TermEscape.te_rv_on = "\033[7m"; 667 TermEscape.te_rv_off = "\033[0m"; 668 } 669 else 670 { 671 TermEscape.te_rv_on = ""; 672 TermEscape.te_rv_off = ""; 673 } 674 } 675 676 if (s == NULL) 677 { 678 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s<null>%s", 679 TermEscape.te_rv_on, TermEscape.te_rv_off); 680 return; 681 } 682 while ((c = (*s++ & 0377)) != '\0') 683 { 684 if (shiftout) 685 { 686 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s", 687 TermEscape.te_rv_off); 688 shiftout = false; 689 } 690 if (!isascii(c)) 691 { 692 if (c == MATCHREPL) 693 { 694 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 695 "%s$", 696 TermEscape.te_rv_on); 697 shiftout = true; 698 if (*s == '\0') 699 continue; 700 c = *s++ & 0377; 701 goto printchar; 702 } 703 if (c == MACROEXPAND || c == MACRODEXPAND) 704 { 705 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 706 "%s$", 707 TermEscape.te_rv_on); 708 if (c == MACRODEXPAND) 709 (void) sm_io_putc(smioout, 710 SM_TIME_DEFAULT, '&'); 711 shiftout = true; 712 if (*s == '\0') 713 continue; 714 if (strchr("=~&?", *s) != NULL) 715 (void) sm_io_putc(smioout, 716 SM_TIME_DEFAULT, 717 *s++); 718 if (bitset(0200, *s)) 719 (void) sm_io_fprintf(smioout, 720 SM_TIME_DEFAULT, 721 "{%s}", 722 macname(bitidx(*s++))); 723 else 724 (void) sm_io_fprintf(smioout, 725 SM_TIME_DEFAULT, 726 "%c", 727 *s++); 728 continue; 729 } 730 for (mp = MetaMacros; mp->metaname != '\0'; mp++) 731 { 732 if (bitidx(mp->metaval) == c) 733 { 734 (void) sm_io_fprintf(smioout, 735 SM_TIME_DEFAULT, 736 "%s$%c", 737 TermEscape.te_rv_on, 738 mp->metaname); 739 shiftout = true; 740 break; 741 } 742 } 743 if (c == MATCHCLASS || c == MATCHNCLASS) 744 { 745 if (bitset(0200, *s)) 746 (void) sm_io_fprintf(smioout, 747 SM_TIME_DEFAULT, 748 "{%s}", 749 macname(bitidx(*s++))); 750 else if (*s != '\0') 751 (void) sm_io_fprintf(smioout, 752 SM_TIME_DEFAULT, 753 "%c", 754 *s++); 755 } 756 if (mp->metaname != '\0') 757 continue; 758 759 /* unrecognized meta character */ 760 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%sM-", 761 TermEscape.te_rv_on); 762 shiftout = true; 763 c &= 0177; 764 } 765 printchar: 766 if (isprint(c)) 767 { 768 (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); 769 continue; 770 } 771 772 /* wasn't a meta-macro -- find another way to print it */ 773 switch (c) 774 { 775 case '\n': 776 c = 'n'; 777 break; 778 779 case '\r': 780 c = 'r'; 781 break; 782 783 case '\t': 784 c = 't'; 785 break; 786 } 787 if (!shiftout) 788 { 789 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s", 790 TermEscape.te_rv_on); 791 shiftout = true; 792 } 793 if (isprint(c)) 794 { 795 (void) sm_io_putc(smioout, SM_TIME_DEFAULT, '\\'); 796 (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); 797 } 798 else 799 { 800 (void) sm_io_putc(smioout, SM_TIME_DEFAULT, '^'); 801 (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c ^ 0100); 802 } 803 } 804 if (shiftout) 805 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s", 806 TermEscape.te_rv_off); 807 (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 808} 809/* 810** MAKELOWER -- Translate a line into lower case 811** 812** Parameters: 813** p -- the string to translate. If NULL, return is 814** immediate. 815** 816** Returns: 817** none. 818** 819** Side Effects: 820** String pointed to by p is translated to lower case. 821*/ 822 823void 824makelower(p) 825 register char *p; 826{ 827 register char c; 828 829 if (p == NULL) 830 return; 831 for (; (c = *p) != '\0'; p++) 832 if (isascii(c) && isupper(c)) 833 *p = tolower(c); 834} 835/* 836** FIXCRLF -- fix <CR><LF> in line. 837** 838** Looks for the <CR><LF> combination and turns it into the 839** UNIX canonical <NL> character. It only takes one line, 840** i.e., it is assumed that the first <NL> found is the end 841** of the line. 842** 843** Parameters: 844** line -- the line to fix. 845** stripnl -- if true, strip the newline also. 846** 847** Returns: 848** none. 849** 850** Side Effects: 851** line is changed in place. 852*/ 853 854void 855fixcrlf(line, stripnl) 856 char *line; 857 bool stripnl; 858{ 859 register char *p; 860 861 p = strchr(line, '\n'); 862 if (p == NULL) 863 return; 864 if (p > line && p[-1] == '\r') 865 p--; 866 if (!stripnl) 867 *p++ = '\n'; 868 *p = '\0'; 869} 870/* 871** PUTLINE -- put a line like fputs obeying SMTP conventions 872** 873** This routine always guarantees outputing a newline (or CRLF, 874** as appropriate) at the end of the string. 875** 876** Parameters: 877** l -- line to put. 878** mci -- the mailer connection information. 879** 880** Returns: 881** none 882** 883** Side Effects: 884** output of l to mci->mci_out. 885*/ 886 887void 888putline(l, mci) 889 register char *l; 890 register MCI *mci; 891{ 892 putxline(l, strlen(l), mci, PXLF_MAPFROM); 893} 894/* 895** PUTXLINE -- putline with flags bits. 896** 897** This routine always guarantees outputing a newline (or CRLF, 898** as appropriate) at the end of the string. 899** 900** Parameters: 901** l -- line to put. 902** len -- the length of the line. 903** mci -- the mailer connection information. 904** pxflags -- flag bits: 905** PXLF_MAPFROM -- map From_ to >From_. 906** PXLF_STRIP8BIT -- strip 8th bit. 907** PXLF_HEADER -- map bare newline in header to newline space. 908** PXLF_NOADDEOL -- don't add an EOL if one wasn't present. 909** 910** Returns: 911** none 912** 913** Side Effects: 914** output of l to mci->mci_out. 915*/ 916 917void 918putxline(l, len, mci, pxflags) 919 register char *l; 920 size_t len; 921 register MCI *mci; 922 int pxflags; 923{ 924 bool dead = false; 925 register char *p, *end; 926 int slop = 0; 927 928 /* strip out 0200 bits -- these can look like TELNET protocol */ 929 if (bitset(MCIF_7BIT, mci->mci_flags) || 930 bitset(PXLF_STRIP8BIT, pxflags)) 931 { 932 register char svchar; 933 934 for (p = l; (svchar = *p) != '\0'; ++p) 935 if (bitset(0200, svchar)) 936 *p = svchar &~ 0200; 937 } 938 939 end = l + len; 940 do 941 { 942 bool noeol = false; 943 944 /* find the end of the line */ 945 p = memchr(l, '\n', end - l); 946 if (p == NULL) 947 { 948 p = end; 949 noeol = true; 950 } 951 952 if (TrafficLogFile != NULL) 953 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 954 "%05d >>> ", (int) CurrentPid); 955 956 /* check for line overflow */ 957 while (mci->mci_mailer->m_linelimit > 0 && 958 (p - l + slop) > mci->mci_mailer->m_linelimit) 959 { 960 char *l_base = l; 961 register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1]; 962 963 if (l[0] == '.' && slop == 0 && 964 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 965 { 966 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 967 '.') == SM_IO_EOF) 968 dead = true; 969 else 970 { 971 /* record progress for DATA timeout */ 972 DataProgress = true; 973 } 974 if (TrafficLogFile != NULL) 975 (void) sm_io_putc(TrafficLogFile, 976 SM_TIME_DEFAULT, '.'); 977 } 978 else if (l[0] == 'F' && slop == 0 && 979 bitset(PXLF_MAPFROM, pxflags) && 980 strncmp(l, "From ", 5) == 0 && 981 bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 982 { 983 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 984 '>') == SM_IO_EOF) 985 dead = true; 986 else 987 { 988 /* record progress for DATA timeout */ 989 DataProgress = true; 990 } 991 if (TrafficLogFile != NULL) 992 (void) sm_io_putc(TrafficLogFile, 993 SM_TIME_DEFAULT, 994 '>'); 995 } 996 if (dead) 997 break; 998 999 while (l < q) 1000 { 1001 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1002 (unsigned char) *l++) == SM_IO_EOF) 1003 { 1004 dead = true; 1005 break; 1006 } 1007 else 1008 { 1009 /* record progress for DATA timeout */ 1010 DataProgress = true; 1011 } 1012 } 1013 if (dead) 1014 break; 1015 1016 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '!') == 1017 SM_IO_EOF || 1018 sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 1019 mci->mci_mailer->m_eol) == 1020 SM_IO_EOF || 1021 sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, ' ') == 1022 SM_IO_EOF) 1023 { 1024 dead = true; 1025 break; 1026 } 1027 else 1028 { 1029 /* record progress for DATA timeout */ 1030 DataProgress = true; 1031 } 1032 if (TrafficLogFile != NULL) 1033 { 1034 for (l = l_base; l < q; l++) 1035 (void) sm_io_putc(TrafficLogFile, 1036 SM_TIME_DEFAULT, 1037 (unsigned char)*l); 1038 (void) sm_io_fprintf(TrafficLogFile, 1039 SM_TIME_DEFAULT, 1040 "!\n%05d >>> ", 1041 (int) CurrentPid); 1042 } 1043 slop = 1; 1044 } 1045 1046 if (dead) 1047 break; 1048 1049 /* output last part */ 1050 if (l[0] == '.' && slop == 0 && 1051 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 1052 { 1053 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '.') == 1054 SM_IO_EOF) 1055 break; 1056 else 1057 { 1058 /* record progress for DATA timeout */ 1059 DataProgress = true; 1060 } 1061 if (TrafficLogFile != NULL) 1062 (void) sm_io_putc(TrafficLogFile, 1063 SM_TIME_DEFAULT, '.'); 1064 } 1065 else if (l[0] == 'F' && slop == 0 && 1066 bitset(PXLF_MAPFROM, pxflags) && 1067 strncmp(l, "From ", 5) == 0 && 1068 bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 1069 { 1070 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '>') == 1071 SM_IO_EOF) 1072 break; 1073 else 1074 { 1075 /* record progress for DATA timeout */ 1076 DataProgress = true; 1077 } 1078 if (TrafficLogFile != NULL) 1079 (void) sm_io_putc(TrafficLogFile, 1080 SM_TIME_DEFAULT, '>'); 1081 } 1082 for ( ; l < p; ++l) 1083 { 1084 if (TrafficLogFile != NULL) 1085 (void) sm_io_putc(TrafficLogFile, 1086 SM_TIME_DEFAULT, 1087 (unsigned char)*l); 1088 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1089 (unsigned char) *l) == SM_IO_EOF) 1090 { 1091 dead = true; 1092 break; 1093 } 1094 else 1095 { 1096 /* record progress for DATA timeout */ 1097 DataProgress = true; 1098 } 1099 } 1100 if (dead) 1101 break; 1102 1103 if (TrafficLogFile != NULL) 1104 (void) sm_io_putc(TrafficLogFile, SM_TIME_DEFAULT, 1105 '\n'); 1106 if ((!bitset(PXLF_NOADDEOL, pxflags) || !noeol) && 1107 sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 1108 mci->mci_mailer->m_eol) == SM_IO_EOF) 1109 break; 1110 else 1111 { 1112 /* record progress for DATA timeout */ 1113 DataProgress = true; 1114 } 1115 if (l < end && *l == '\n') 1116 { 1117 if (*++l != ' ' && *l != '\t' && *l != '\0' && 1118 bitset(PXLF_HEADER, pxflags)) 1119 { 1120 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1121 ' ') == SM_IO_EOF) 1122 break; 1123 else 1124 { 1125 /* record progress for DATA timeout */ 1126 DataProgress = true; 1127 } 1128 1129 if (TrafficLogFile != NULL) 1130 (void) sm_io_putc(TrafficLogFile, 1131 SM_TIME_DEFAULT, ' '); 1132 } 1133 } 1134 1135 /* record progress for DATA timeout */ 1136 DataProgress = true; 1137 } while (l < end); 1138} 1139/* 1140** XUNLINK -- unlink a file, doing logging as appropriate. 1141** 1142** Parameters: 1143** f -- name of file to unlink. 1144** 1145** Returns: 1146** return value of unlink() 1147** 1148** Side Effects: 1149** f is unlinked. 1150*/ 1151 1152int 1153xunlink(f) 1154 char *f; 1155{ 1156 register int i; 1157 int save_errno; 1158 1159 if (LogLevel > 98) 1160 sm_syslog(LOG_DEBUG, CurEnv->e_id, "unlink %s", f); 1161 1162 i = unlink(f); 1163 save_errno = errno; 1164 if (i < 0 && LogLevel > 97) 1165 sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s: unlink-fail %d", 1166 f, errno); 1167 if (i >= 0) 1168 SYNC_DIR(f, false); 1169 errno = save_errno; 1170 return i; 1171} 1172/* 1173** SFGETS -- "safe" fgets -- times out and ignores random interrupts. 1174** 1175** Parameters: 1176** buf -- place to put the input line. 1177** siz -- size of buf. 1178** fp -- file to read from. 1179** timeout -- the timeout before error occurs. 1180** during -- what we are trying to read (for error messages). 1181** 1182** Returns: 1183** NULL on error (including timeout). This may also leave 1184** buf containing a null string. 1185** buf otherwise. 1186*/ 1187 1188 1189char * 1190sfgets(buf, siz, fp, timeout, during) 1191 char *buf; 1192 int siz; 1193 SM_FILE_T *fp; 1194 time_t timeout; 1195 char *during; 1196{ 1197 register char *p; 1198 int save_errno; 1199 int io_timeout; 1200 1201 SM_REQUIRE(siz > 0); 1202 SM_REQUIRE(buf != NULL); 1203 1204 if (fp == NULL) 1205 { 1206 buf[0] = '\0'; 1207 errno = EBADF; 1208 return NULL; 1209 } 1210 1211 /* try to read */ 1212 p = NULL; 1213 errno = 0; 1214 1215 /* convert the timeout to sm_io notation */ 1216 io_timeout = (timeout <= 0) ? SM_TIME_DEFAULT : timeout * 1000; 1217 while (!sm_io_eof(fp) && !sm_io_error(fp)) 1218 { 1219 errno = 0; 1220 p = sm_io_fgets(fp, io_timeout, buf, siz); 1221 if (p == NULL && errno == EAGAIN) 1222 { 1223 /* The sm_io_fgets() call timedout */ 1224 if (LogLevel > 1) 1225 sm_syslog(LOG_NOTICE, CurEnv->e_id, 1226 "timeout waiting for input from %.100s during %s", 1227 CURHOSTNAME, 1228 during); 1229 buf[0] = '\0'; 1230#if XDEBUG 1231 checkfd012(during); 1232#endif /* XDEBUG */ 1233 if (TrafficLogFile != NULL) 1234 (void) sm_io_fprintf(TrafficLogFile, 1235 SM_TIME_DEFAULT, 1236 "%05d <<< [TIMEOUT]\n", 1237 (int) CurrentPid); 1238 errno = ETIMEDOUT; 1239 return NULL; 1240 } 1241 if (p != NULL || errno != EINTR) 1242 break; 1243 (void) sm_io_clearerr(fp); 1244 } 1245 save_errno = errno; 1246 1247 /* clean up the books and exit */ 1248 LineNumber++; 1249 if (p == NULL) 1250 { 1251 buf[0] = '\0'; 1252 if (TrafficLogFile != NULL) 1253 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 1254 "%05d <<< [EOF]\n", 1255 (int) CurrentPid); 1256 errno = save_errno; 1257 return NULL; 1258 } 1259 if (TrafficLogFile != NULL) 1260 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 1261 "%05d <<< %s", (int) CurrentPid, buf); 1262 if (SevenBitInput) 1263 { 1264 for (p = buf; *p != '\0'; p++) 1265 *p &= ~0200; 1266 } 1267 else if (!HasEightBits) 1268 { 1269 for (p = buf; *p != '\0'; p++) 1270 { 1271 if (bitset(0200, *p)) 1272 { 1273 HasEightBits = true; 1274 break; 1275 } 1276 } 1277 } 1278 return buf; 1279} 1280/* 1281** FGETFOLDED -- like fgets, but knows about folded lines. 1282** 1283** Parameters: 1284** buf -- place to put result. 1285** n -- bytes available. 1286** f -- file to read from. 1287** 1288** Returns: 1289** input line(s) on success, NULL on error or SM_IO_EOF. 1290** This will normally be buf -- unless the line is too 1291** long, when it will be sm_malloc_x()ed. 1292** 1293** Side Effects: 1294** buf gets lines from f, with continuation lines (lines 1295** with leading white space) appended. CRLF's are mapped 1296** into single newlines. Any trailing NL is stripped. 1297*/ 1298 1299char * 1300fgetfolded(buf, n, f) 1301 char *buf; 1302 register int n; 1303 SM_FILE_T *f; 1304{ 1305 register char *p = buf; 1306 char *bp = buf; 1307 register int i; 1308 1309 SM_REQUIRE(n > 0); 1310 SM_REQUIRE(buf != NULL); 1311 if (f == NULL) 1312 { 1313 buf[0] = '\0'; 1314 errno = EBADF; 1315 return NULL; 1316 } 1317 1318 n--; 1319 while ((i = sm_io_getc(f, SM_TIME_DEFAULT)) != SM_IO_EOF) 1320 { 1321 if (i == '\r') 1322 { 1323 i = sm_io_getc(f, SM_TIME_DEFAULT); 1324 if (i != '\n') 1325 { 1326 if (i != SM_IO_EOF) 1327 (void) sm_io_ungetc(f, SM_TIME_DEFAULT, 1328 i); 1329 i = '\r'; 1330 } 1331 } 1332 if (--n <= 0) 1333 { 1334 /* allocate new space */ 1335 char *nbp; 1336 int nn; 1337 1338 nn = (p - bp); 1339 if (nn < MEMCHUNKSIZE) 1340 nn *= 2; 1341 else 1342 nn += MEMCHUNKSIZE; 1343 nbp = sm_malloc_x(nn); 1344 memmove(nbp, bp, p - bp); 1345 p = &nbp[p - bp]; 1346 if (bp != buf) 1347 sm_free(bp); 1348 bp = nbp; 1349 n = nn - (p - bp); 1350 } 1351 *p++ = i; 1352 if (i == '\n') 1353 { 1354 LineNumber++; 1355 i = sm_io_getc(f, SM_TIME_DEFAULT); 1356 if (i != SM_IO_EOF) 1357 (void) sm_io_ungetc(f, SM_TIME_DEFAULT, i); 1358 if (i != ' ' && i != '\t') 1359 break; 1360 } 1361 } 1362 if (p == bp) 1363 return NULL; 1364 if (p[-1] == '\n') 1365 p--; 1366 *p = '\0'; 1367 return bp; 1368} 1369/* 1370** CURTIME -- return current time. 1371** 1372** Parameters: 1373** none. 1374** 1375** Returns: 1376** the current time. 1377*/ 1378 1379time_t 1380curtime() 1381{ 1382 auto time_t t; 1383 1384 (void) time(&t); 1385 return t; 1386} 1387/* 1388** ATOBOOL -- convert a string representation to boolean. 1389** 1390** Defaults to false 1391** 1392** Parameters: 1393** s -- string to convert. Takes "tTyY", empty, and NULL as true, 1394** others as false. 1395** 1396** Returns: 1397** A boolean representation of the string. 1398*/ 1399 1400bool 1401atobool(s) 1402 register char *s; 1403{ 1404 if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL) 1405 return true; 1406 return false; 1407} 1408/* 1409** ATOOCT -- convert a string representation to octal. 1410** 1411** Parameters: 1412** s -- string to convert. 1413** 1414** Returns: 1415** An integer representing the string interpreted as an 1416** octal number. 1417*/ 1418 1419int 1420atooct(s) 1421 register char *s; 1422{ 1423 register int i = 0; 1424 1425 while (*s >= '0' && *s <= '7') 1426 i = (i << 3) | (*s++ - '0'); 1427 return i; 1428} 1429/* 1430** BITINTERSECT -- tell if two bitmaps intersect 1431** 1432** Parameters: 1433** a, b -- the bitmaps in question 1434** 1435** Returns: 1436** true if they have a non-null intersection 1437** false otherwise 1438*/ 1439 1440bool 1441bitintersect(a, b) 1442 BITMAP256 a; 1443 BITMAP256 b; 1444{ 1445 int i; 1446 1447 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1448 { 1449 if ((a[i] & b[i]) != 0) 1450 return true; 1451 } 1452 return false; 1453} 1454/* 1455** BITZEROP -- tell if a bitmap is all zero 1456** 1457** Parameters: 1458** map -- the bit map to check 1459** 1460** Returns: 1461** true if map is all zero. 1462** false if there are any bits set in map. 1463*/ 1464 1465bool 1466bitzerop(map) 1467 BITMAP256 map; 1468{ 1469 int i; 1470 1471 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1472 { 1473 if (map[i] != 0) 1474 return false; 1475 } 1476 return true; 1477} 1478/* 1479** STRCONTAINEDIN -- tell if one string is contained in another 1480** 1481** Parameters: 1482** icase -- ignore case? 1483** a -- possible substring. 1484** b -- possible superstring. 1485** 1486** Returns: 1487** true if a is contained in b (case insensitive). 1488** false otherwise. 1489*/ 1490 1491bool 1492strcontainedin(icase, a, b) 1493 bool icase; 1494 register char *a; 1495 register char *b; 1496{ 1497 int la; 1498 int lb; 1499 int c; 1500 1501 la = strlen(a); 1502 lb = strlen(b); 1503 c = *a; 1504 if (icase && isascii(c) && isupper(c)) 1505 c = tolower(c); 1506 for (; lb-- >= la; b++) 1507 { 1508 if (icase) 1509 { 1510 if (*b != c && 1511 isascii(*b) && isupper(*b) && tolower(*b) != c) 1512 continue; 1513 if (sm_strncasecmp(a, b, la) == 0) 1514 return true; 1515 } 1516 else 1517 { 1518 if (*b != c) 1519 continue; 1520 if (strncmp(a, b, la) == 0) 1521 return true; 1522 } 1523 } 1524 return false; 1525} 1526/* 1527** CHECKFD012 -- check low numbered file descriptors 1528** 1529** File descriptors 0, 1, and 2 should be open at all times. 1530** This routine verifies that, and fixes it if not true. 1531** 1532** Parameters: 1533** where -- a tag printed if the assertion failed 1534** 1535** Returns: 1536** none 1537*/ 1538 1539void 1540checkfd012(where) 1541 char *where; 1542{ 1543#if XDEBUG 1544 register int i; 1545 1546 for (i = 0; i < 3; i++) 1547 fill_fd(i, where); 1548#endif /* XDEBUG */ 1549} 1550/* 1551** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging 1552** 1553** Parameters: 1554** fd -- file descriptor to check. 1555** where -- tag to print on failure. 1556** 1557** Returns: 1558** none. 1559*/ 1560 1561void 1562checkfdopen(fd, where) 1563 int fd; 1564 char *where; 1565{ 1566#if XDEBUG 1567 struct stat st; 1568 1569 if (fstat(fd, &st) < 0 && errno == EBADF) 1570 { 1571 syserr("checkfdopen(%d): %s not open as expected!", fd, where); 1572 printopenfds(true); 1573 } 1574#endif /* XDEBUG */ 1575} 1576/* 1577** CHECKFDS -- check for new or missing file descriptors 1578** 1579** Parameters: 1580** where -- tag for printing. If null, take a base line. 1581** 1582** Returns: 1583** none 1584** 1585** Side Effects: 1586** If where is set, shows changes since the last call. 1587*/ 1588 1589void 1590checkfds(where) 1591 char *where; 1592{ 1593 int maxfd; 1594 register int fd; 1595 bool printhdr = true; 1596 int save_errno = errno; 1597 static BITMAP256 baseline; 1598 extern int DtableSize; 1599 1600 if (DtableSize > BITMAPBITS) 1601 maxfd = BITMAPBITS; 1602 else 1603 maxfd = DtableSize; 1604 if (where == NULL) 1605 clrbitmap(baseline); 1606 1607 for (fd = 0; fd < maxfd; fd++) 1608 { 1609 struct stat stbuf; 1610 1611 if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP) 1612 { 1613 if (!bitnset(fd, baseline)) 1614 continue; 1615 clrbitn(fd, baseline); 1616 } 1617 else if (!bitnset(fd, baseline)) 1618 setbitn(fd, baseline); 1619 else 1620 continue; 1621 1622 /* file state has changed */ 1623 if (where == NULL) 1624 continue; 1625 if (printhdr) 1626 { 1627 sm_syslog(LOG_DEBUG, CurEnv->e_id, 1628 "%s: changed fds:", 1629 where); 1630 printhdr = false; 1631 } 1632 dumpfd(fd, true, true); 1633 } 1634 errno = save_errno; 1635} 1636/* 1637** PRINTOPENFDS -- print the open file descriptors (for debugging) 1638** 1639** Parameters: 1640** logit -- if set, send output to syslog; otherwise 1641** print for debugging. 1642** 1643** Returns: 1644** none. 1645*/ 1646 1647#if NETINET || NETINET6 1648# include <arpa/inet.h> 1649#endif /* NETINET || NETINET6 */ 1650 1651void 1652printopenfds(logit) 1653 bool logit; 1654{ 1655 register int fd; 1656 extern int DtableSize; 1657 1658 for (fd = 0; fd < DtableSize; fd++) 1659 dumpfd(fd, false, logit); 1660} 1661/* 1662** DUMPFD -- dump a file descriptor 1663** 1664** Parameters: 1665** fd -- the file descriptor to dump. 1666** printclosed -- if set, print a notification even if 1667** it is closed; otherwise print nothing. 1668** logit -- if set, send output to syslog instead of stdout. 1669** 1670** Returns: 1671** none. 1672*/ 1673 1674void 1675dumpfd(fd, printclosed, logit) 1676 int fd; 1677 bool printclosed; 1678 bool logit; 1679{ 1680 register char *p; 1681 char *hp; 1682#ifdef S_IFSOCK 1683 SOCKADDR sa; 1684#endif /* S_IFSOCK */ 1685 auto SOCKADDR_LEN_T slen; 1686 int i; 1687#if STAT64 > 0 1688 struct stat64 st; 1689#else /* STAT64 > 0 */ 1690 struct stat st; 1691#endif /* STAT64 > 0 */ 1692 char buf[200]; 1693 1694 p = buf; 1695 (void) sm_snprintf(p, SPACELEFT(buf, p), "%3d: ", fd); 1696 p += strlen(p); 1697 1698 if ( 1699#if STAT64 > 0 1700 fstat64(fd, &st) 1701#else /* STAT64 > 0 */ 1702 fstat(fd, &st) 1703#endif /* STAT64 > 0 */ 1704 < 0) 1705 { 1706 if (errno != EBADF) 1707 { 1708 (void) sm_snprintf(p, SPACELEFT(buf, p), 1709 "CANNOT STAT (%s)", 1710 sm_errstring(errno)); 1711 goto printit; 1712 } 1713 else if (printclosed) 1714 { 1715 (void) sm_snprintf(p, SPACELEFT(buf, p), "CLOSED"); 1716 goto printit; 1717 } 1718 return; 1719 } 1720 1721 i = fcntl(fd, F_GETFL, 0); 1722 if (i != -1) 1723 { 1724 (void) sm_snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i); 1725 p += strlen(p); 1726 } 1727 1728 (void) sm_snprintf(p, SPACELEFT(buf, p), "mode=%o: ", 1729 (int) st.st_mode); 1730 p += strlen(p); 1731 switch (st.st_mode & S_IFMT) 1732 { 1733#ifdef S_IFSOCK 1734 case S_IFSOCK: 1735 (void) sm_snprintf(p, SPACELEFT(buf, p), "SOCK "); 1736 p += strlen(p); 1737 memset(&sa, '\0', sizeof sa); 1738 slen = sizeof sa; 1739 if (getsockname(fd, &sa.sa, &slen) < 0) 1740 (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)", 1741 sm_errstring(errno)); 1742 else 1743 { 1744 hp = hostnamebyanyaddr(&sa); 1745 if (hp == NULL) 1746 { 1747 /* EMPTY */ 1748 /* do nothing */ 1749 } 1750# if NETINET 1751 else if (sa.sa.sa_family == AF_INET) 1752 (void) sm_snprintf(p, SPACELEFT(buf, p), 1753 "%s/%d", hp, ntohs(sa.sin.sin_port)); 1754# endif /* NETINET */ 1755# if NETINET6 1756 else if (sa.sa.sa_family == AF_INET6) 1757 (void) sm_snprintf(p, SPACELEFT(buf, p), 1758 "%s/%d", hp, ntohs(sa.sin6.sin6_port)); 1759# endif /* NETINET6 */ 1760 else 1761 (void) sm_snprintf(p, SPACELEFT(buf, p), 1762 "%s", hp); 1763 } 1764 p += strlen(p); 1765 (void) sm_snprintf(p, SPACELEFT(buf, p), "->"); 1766 p += strlen(p); 1767 slen = sizeof sa; 1768 if (getpeername(fd, &sa.sa, &slen) < 0) 1769 (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)", 1770 sm_errstring(errno)); 1771 else 1772 { 1773 hp = hostnamebyanyaddr(&sa); 1774 if (hp == NULL) 1775 { 1776 /* EMPTY */ 1777 /* do nothing */ 1778 } 1779# if NETINET 1780 else if (sa.sa.sa_family == AF_INET) 1781 (void) sm_snprintf(p, SPACELEFT(buf, p), 1782 "%s/%d", hp, ntohs(sa.sin.sin_port)); 1783# endif /* NETINET */ 1784# if NETINET6 1785 else if (sa.sa.sa_family == AF_INET6) 1786 (void) sm_snprintf(p, SPACELEFT(buf, p), 1787 "%s/%d", hp, ntohs(sa.sin6.sin6_port)); 1788# endif /* NETINET6 */ 1789 else 1790 (void) sm_snprintf(p, SPACELEFT(buf, p), 1791 "%s", hp); 1792 } 1793 break; 1794#endif /* S_IFSOCK */ 1795 1796 case S_IFCHR: 1797 (void) sm_snprintf(p, SPACELEFT(buf, p), "CHR: "); 1798 p += strlen(p); 1799 goto defprint; 1800 1801#ifdef S_IFBLK 1802 case S_IFBLK: 1803 (void) sm_snprintf(p, SPACELEFT(buf, p), "BLK: "); 1804 p += strlen(p); 1805 goto defprint; 1806#endif /* S_IFBLK */ 1807 1808#if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) 1809 case S_IFIFO: 1810 (void) sm_snprintf(p, SPACELEFT(buf, p), "FIFO: "); 1811 p += strlen(p); 1812 goto defprint; 1813#endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */ 1814 1815#ifdef S_IFDIR 1816 case S_IFDIR: 1817 (void) sm_snprintf(p, SPACELEFT(buf, p), "DIR: "); 1818 p += strlen(p); 1819 goto defprint; 1820#endif /* S_IFDIR */ 1821 1822#ifdef S_IFLNK 1823 case S_IFLNK: 1824 (void) sm_snprintf(p, SPACELEFT(buf, p), "LNK: "); 1825 p += strlen(p); 1826 goto defprint; 1827#endif /* S_IFLNK */ 1828 1829 default: 1830defprint: 1831 (void) sm_snprintf(p, SPACELEFT(buf, p), 1832 "dev=%d/%d, ino=%llu, nlink=%d, u/gid=%d/%d, ", 1833 major(st.st_dev), minor(st.st_dev), 1834 (ULONGLONG_T) st.st_ino, 1835 (int) st.st_nlink, (int) st.st_uid, 1836 (int) st.st_gid); 1837 p += strlen(p); 1838 (void) sm_snprintf(p, SPACELEFT(buf, p), "size=%llu", 1839 (ULONGLONG_T) st.st_size); 1840 break; 1841 } 1842 1843printit: 1844 if (logit) 1845 sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL, 1846 "%.800s", buf); 1847 else 1848 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", buf); 1849} 1850/* 1851** SHORTEN_HOSTNAME -- strip local domain information off of hostname. 1852** 1853** Parameters: 1854** host -- the host to shorten (stripped in place). 1855** 1856** Returns: 1857** place where string was truncated, NULL if not truncated. 1858*/ 1859 1860char * 1861shorten_hostname(host) 1862 char host[]; 1863{ 1864 register char *p; 1865 char *mydom; 1866 int i; 1867 bool canon = false; 1868 1869 /* strip off final dot */ 1870 i = strlen(host); 1871 p = &host[(i == 0) ? 0 : i - 1]; 1872 if (*p == '.') 1873 { 1874 *p = '\0'; 1875 canon = true; 1876 } 1877 1878 /* see if there is any domain at all -- if not, we are done */ 1879 p = strchr(host, '.'); 1880 if (p == NULL) 1881 return NULL; 1882 1883 /* yes, we have a domain -- see if it looks like us */ 1884 mydom = macvalue('m', CurEnv); 1885 if (mydom == NULL) 1886 mydom = ""; 1887 i = strlen(++p); 1888 if ((canon ? sm_strcasecmp(p, mydom) 1889 : sm_strncasecmp(p, mydom, i)) == 0 && 1890 (mydom[i] == '.' || mydom[i] == '\0')) 1891 { 1892 *--p = '\0'; 1893 return p; 1894 } 1895 return NULL; 1896} 1897/* 1898** PROG_OPEN -- open a program for reading 1899** 1900** Parameters: 1901** argv -- the argument list. 1902** pfd -- pointer to a place to store the file descriptor. 1903** e -- the current envelope. 1904** 1905** Returns: 1906** pid of the process -- -1 if it failed. 1907*/ 1908 1909pid_t 1910prog_open(argv, pfd, e) 1911 char **argv; 1912 int *pfd; 1913 ENVELOPE *e; 1914{ 1915 pid_t pid; 1916 int i; 1917 int save_errno; 1918 int sff; 1919 int ret; 1920 int fdv[2]; 1921 char *p, *q; 1922 char buf[MAXLINE + 1]; 1923 extern int DtableSize; 1924 1925 if (pipe(fdv) < 0) 1926 { 1927 syserr("%s: cannot create pipe for stdout", argv[0]); 1928 return -1; 1929 } 1930 pid = fork(); 1931 if (pid < 0) 1932 { 1933 syserr("%s: cannot fork", argv[0]); 1934 (void) close(fdv[0]); 1935 (void) close(fdv[1]); 1936 return -1; 1937 } 1938 if (pid > 0) 1939 { 1940 /* parent */ 1941 (void) close(fdv[1]); 1942 *pfd = fdv[0]; 1943 return pid; 1944 } 1945 1946 /* Reset global flags */ 1947 RestartRequest = NULL; 1948 RestartWorkGroup = false; 1949 ShutdownRequest = NULL; 1950 PendingSignal = 0; 1951 CurrentPid = getpid(); 1952 1953 /* 1954 ** Initialize exception stack and default exception 1955 ** handler for child process. 1956 */ 1957 1958 sm_exc_newthread(fatal_error); 1959 1960 /* child -- close stdin */ 1961 (void) close(0); 1962 1963 /* stdout goes back to parent */ 1964 (void) close(fdv[0]); 1965 if (dup2(fdv[1], 1) < 0) 1966 { 1967 syserr("%s: cannot dup2 for stdout", argv[0]); 1968 _exit(EX_OSERR); 1969 } 1970 (void) close(fdv[1]); 1971 1972 /* stderr goes to transcript if available */ 1973 if (e->e_xfp != NULL) 1974 { 1975 int xfd; 1976 1977 xfd = sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL); 1978 if (xfd >= 0 && dup2(xfd, 2) < 0) 1979 { 1980 syserr("%s: cannot dup2 for stderr", argv[0]); 1981 _exit(EX_OSERR); 1982 } 1983 } 1984 1985 /* this process has no right to the queue file */ 1986 if (e->e_lockfp != NULL) 1987 (void) close(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL)); 1988 1989 /* chroot to the program mailer directory, if defined */ 1990 if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL) 1991 { 1992 expand(ProgMailer->m_rootdir, buf, sizeof buf, e); 1993 if (chroot(buf) < 0) 1994 { 1995 syserr("prog_open: cannot chroot(%s)", buf); 1996 exit(EX_TEMPFAIL); 1997 } 1998 if (chdir("/") < 0) 1999 { 2000 syserr("prog_open: cannot chdir(/)"); 2001 exit(EX_TEMPFAIL); 2002 } 2003 } 2004 2005 /* run as default user */ 2006 endpwent(); 2007 sm_mbdb_terminate(); 2008 if (setgid(DefGid) < 0 && geteuid() == 0) 2009 { 2010 syserr("prog_open: setgid(%ld) failed", (long) DefGid); 2011 exit(EX_TEMPFAIL); 2012 } 2013 if (setuid(DefUid) < 0 && geteuid() == 0) 2014 { 2015 syserr("prog_open: setuid(%ld) failed", (long) DefUid); 2016 exit(EX_TEMPFAIL); 2017 } 2018 2019 /* run in some directory */ 2020 if (ProgMailer != NULL) 2021 p = ProgMailer->m_execdir; 2022 else 2023 p = NULL; 2024 for (; p != NULL; p = q) 2025 { 2026 q = strchr(p, ':'); 2027 if (q != NULL) 2028 *q = '\0'; 2029 expand(p, buf, sizeof buf, e); 2030 if (q != NULL) 2031 *q++ = ':'; 2032 if (buf[0] != '\0' && chdir(buf) >= 0) 2033 break; 2034 } 2035 if (p == NULL) 2036 { 2037 /* backup directories */ 2038 if (chdir("/tmp") < 0) 2039 (void) chdir("/"); 2040 } 2041 2042 /* Check safety of program to be run */ 2043 sff = SFF_ROOTOK|SFF_EXECOK; 2044 if (!bitnset(DBS_RUNWRITABLEPROGRAM, DontBlameSendmail)) 2045 sff |= SFF_NOGWFILES|SFF_NOWWFILES; 2046 if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, DontBlameSendmail)) 2047 sff |= SFF_NOPATHCHECK; 2048 else 2049 sff |= SFF_SAFEDIRPATH; 2050 ret = safefile(argv[0], DefUid, DefGid, DefUser, sff, 0, NULL); 2051 if (ret != 0) 2052 sm_syslog(LOG_INFO, e->e_id, 2053 "Warning: prog_open: program %s unsafe: %s", 2054 argv[0], sm_errstring(ret)); 2055 2056 /* arrange for all the files to be closed */ 2057 for (i = 3; i < DtableSize; i++) 2058 { 2059 register int j; 2060 2061 if ((j = fcntl(i, F_GETFD, 0)) != -1) 2062 (void) fcntl(i, F_SETFD, j | FD_CLOEXEC); 2063 } 2064 2065 /* now exec the process */ 2066 (void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron); 2067 2068 /* woops! failed */ 2069 save_errno = errno; 2070 syserr("%s: cannot exec", argv[0]); 2071 if (transienterror(save_errno)) 2072 _exit(EX_OSERR); 2073 _exit(EX_CONFIG); 2074 return -1; /* avoid compiler warning on IRIX */ 2075} 2076/* 2077** GET_COLUMN -- look up a Column in a line buffer 2078** 2079** Parameters: 2080** line -- the raw text line to search. 2081** col -- the column number to fetch. 2082** delim -- the delimiter between columns. If null, 2083** use white space. 2084** buf -- the output buffer. 2085** buflen -- the length of buf. 2086** 2087** Returns: 2088** buf if successful. 2089** NULL otherwise. 2090*/ 2091 2092char * 2093get_column(line, col, delim, buf, buflen) 2094 char line[]; 2095 int col; 2096 int delim; 2097 char buf[]; 2098 int buflen; 2099{ 2100 char *p; 2101 char *begin, *end; 2102 int i; 2103 char delimbuf[4]; 2104 2105 if ((char) delim == '\0') 2106 (void) sm_strlcpy(delimbuf, "\n\t ", sizeof delimbuf); 2107 else 2108 { 2109 delimbuf[0] = (char) delim; 2110 delimbuf[1] = '\0'; 2111 } 2112 2113 p = line; 2114 if (*p == '\0') 2115 return NULL; /* line empty */ 2116 if (*p == (char) delim && col == 0) 2117 return NULL; /* first column empty */ 2118 2119 begin = line; 2120 2121 if (col == 0 && (char) delim == '\0') 2122 { 2123 while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 2124 begin++; 2125 } 2126 2127 for (i = 0; i < col; i++) 2128 { 2129 if ((begin = strpbrk(begin, delimbuf)) == NULL) 2130 return NULL; /* no such column */ 2131 begin++; 2132 if ((char) delim == '\0') 2133 { 2134 while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 2135 begin++; 2136 } 2137 } 2138 2139 end = strpbrk(begin, delimbuf); 2140 if (end == NULL) 2141 i = strlen(begin); 2142 else 2143 i = end - begin; 2144 if (i >= buflen) 2145 i = buflen - 1; 2146 (void) sm_strlcpy(buf, begin, i + 1); 2147 return buf; 2148} 2149/* 2150** CLEANSTRCPY -- copy string keeping out bogus characters 2151** 2152** Parameters: 2153** t -- "to" string. 2154** f -- "from" string. 2155** l -- length of space available in "to" string. 2156** 2157** Returns: 2158** none. 2159*/ 2160 2161void 2162cleanstrcpy(t, f, l) 2163 register char *t; 2164 register char *f; 2165 int l; 2166{ 2167 /* check for newlines and log if necessary */ 2168 (void) denlstring(f, true, true); 2169 2170 if (l <= 0) 2171 syserr("!cleanstrcpy: length == 0"); 2172 2173 l--; 2174 while (l > 0 && *f != '\0') 2175 { 2176 if (isascii(*f) && 2177 (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL)) 2178 { 2179 l--; 2180 *t++ = *f; 2181 } 2182 f++; 2183 } 2184 *t = '\0'; 2185} 2186/* 2187** DENLSTRING -- convert newlines in a string to spaces 2188** 2189** Parameters: 2190** s -- the input string 2191** strict -- if set, don't permit continuation lines. 2192** logattacks -- if set, log attempted attacks. 2193** 2194** Returns: 2195** A pointer to a version of the string with newlines 2196** mapped to spaces. This should be copied. 2197*/ 2198 2199char * 2200denlstring(s, strict, logattacks) 2201 char *s; 2202 bool strict; 2203 bool logattacks; 2204{ 2205 register char *p; 2206 int l; 2207 static char *bp = NULL; 2208 static int bl = 0; 2209 2210 p = s; 2211 while ((p = strchr(p, '\n')) != NULL) 2212 if (strict || (*++p != ' ' && *p != '\t')) 2213 break; 2214 if (p == NULL) 2215 return s; 2216 2217 l = strlen(s) + 1; 2218 if (bl < l) 2219 { 2220 /* allocate more space */ 2221 char *nbp = sm_pmalloc_x(l); 2222 2223 if (bp != NULL) 2224 sm_free(bp); 2225 bp = nbp; 2226 bl = l; 2227 } 2228 (void) sm_strlcpy(bp, s, l); 2229 for (p = bp; (p = strchr(p, '\n')) != NULL; ) 2230 *p++ = ' '; 2231 2232 if (logattacks) 2233 { 2234 sm_syslog(LOG_NOTICE, CurEnv->e_id, 2235 "POSSIBLE ATTACK from %.100s: newline in string \"%s\"", 2236 RealHostName == NULL ? "[UNKNOWN]" : RealHostName, 2237 shortenstring(bp, MAXSHORTSTR)); 2238 } 2239 2240 return bp; 2241} 2242/* 2243** STR2PRT -- convert "unprintable" characters in a string to \oct 2244** 2245** Parameters: 2246** s -- string to convert 2247** 2248** Returns: 2249** converted string. 2250** This is a static local buffer, string must be copied 2251** before this function is called again! 2252*/ 2253 2254char * 2255str2prt(s) 2256 char *s; 2257{ 2258 int l; 2259 char c, *h; 2260 bool ok; 2261 static int len = 0; 2262 static char *buf = NULL; 2263 2264 if (s == NULL) 2265 return NULL; 2266 ok = true; 2267 for (h = s, l = 1; *h != '\0'; h++, l++) 2268 { 2269 if (*h == '\\') 2270 { 2271 ++l; 2272 ok = false; 2273 } 2274 else if (!(isascii(*h) && isprint(*h))) 2275 { 2276 l += 3; 2277 ok = false; 2278 } 2279 } 2280 if (ok) 2281 return s; 2282 if (l > len) 2283 { 2284 char *nbuf = sm_pmalloc_x(l); 2285 2286 if (buf != NULL) 2287 sm_free(buf); 2288 len = l; 2289 buf = nbuf; 2290 } 2291 for (h = buf; *s != '\0' && l > 0; s++, l--) 2292 { 2293 c = *s; 2294 if (isascii(c) && isprint(c) && c != '\\') 2295 { 2296 *h++ = c; 2297 } 2298 else 2299 { 2300 *h++ = '\\'; 2301 --l; 2302 switch (c) 2303 { 2304 case '\\': 2305 *h++ = '\\'; 2306 break; 2307 case '\t': 2308 *h++ = 't'; 2309 break; 2310 case '\n': 2311 *h++ = 'n'; 2312 break; 2313 case '\r': 2314 *h++ = 'r'; 2315 break; 2316 default: 2317 (void) sm_snprintf(h, l, "%03o", (int) c); 2318 2319 /* 2320 ** XXX since l is unsigned this may 2321 ** wrap around if the calculation is screwed 2322 ** up... 2323 */ 2324 2325 l -= 2; 2326 h += 3; 2327 break; 2328 } 2329 } 2330 } 2331 *h = '\0'; 2332 buf[len - 1] = '\0'; 2333 return buf; 2334} 2335/* 2336** PATH_IS_DIR -- check to see if file exists and is a directory. 2337** 2338** There are some additional checks for security violations in 2339** here. This routine is intended to be used for the host status 2340** support. 2341** 2342** Parameters: 2343** pathname -- pathname to check for directory-ness. 2344** createflag -- if set, create directory if needed. 2345** 2346** Returns: 2347** true -- if the indicated pathname is a directory 2348** false -- otherwise 2349*/ 2350 2351int 2352path_is_dir(pathname, createflag) 2353 char *pathname; 2354 bool createflag; 2355{ 2356 struct stat statbuf; 2357 2358#if HASLSTAT 2359 if (lstat(pathname, &statbuf) < 0) 2360#else /* HASLSTAT */ 2361 if (stat(pathname, &statbuf) < 0) 2362#endif /* HASLSTAT */ 2363 { 2364 if (errno != ENOENT || !createflag) 2365 return false; 2366 if (mkdir(pathname, 0755) < 0) 2367 return false; 2368 return true; 2369 } 2370 if (!S_ISDIR(statbuf.st_mode)) 2371 { 2372 errno = ENOTDIR; 2373 return false; 2374 } 2375 2376 /* security: don't allow writable directories */ 2377 if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode)) 2378 { 2379 errno = EACCES; 2380 return false; 2381 } 2382 return true; 2383} 2384/* 2385** PROC_LIST_ADD -- add process id to list of our children 2386** 2387** Parameters: 2388** pid -- pid to add to list. 2389** task -- task of pid. 2390** type -- type of process. 2391** count -- number of processes. 2392** other -- other information for this type. 2393** 2394** Returns: 2395** none 2396** 2397** Side Effects: 2398** May increase CurChildren. May grow ProcList. 2399*/ 2400 2401typedef struct procs PROCS_T; 2402 2403struct procs 2404{ 2405 pid_t proc_pid; 2406 char *proc_task; 2407 int proc_type; 2408 int proc_count; 2409 int proc_other; 2410}; 2411 2412static PROCS_T *volatile ProcListVec = NULL; 2413static int ProcListSize = 0; 2414 2415void 2416proc_list_add(pid, task, type, count, other) 2417 pid_t pid; 2418 char *task; 2419 int type; 2420 int count; 2421 int other; 2422{ 2423 int i; 2424 2425 for (i = 0; i < ProcListSize; i++) 2426 { 2427 if (ProcListVec[i].proc_pid == NO_PID) 2428 break; 2429 } 2430 if (i >= ProcListSize) 2431 { 2432 /* probe the existing vector to avoid growing infinitely */ 2433 proc_list_probe(); 2434 2435 /* now scan again */ 2436 for (i = 0; i < ProcListSize; i++) 2437 { 2438 if (ProcListVec[i].proc_pid == NO_PID) 2439 break; 2440 } 2441 } 2442 if (i >= ProcListSize) 2443 { 2444 /* grow process list */ 2445 PROCS_T *npv; 2446 2447 SM_ASSERT(ProcListSize < INT_MAX - PROC_LIST_SEG); 2448 npv = (PROCS_T *) sm_pmalloc_x((sizeof *npv) * 2449 (ProcListSize + PROC_LIST_SEG)); 2450 if (ProcListSize > 0) 2451 { 2452 memmove(npv, ProcListVec, 2453 ProcListSize * sizeof (PROCS_T)); 2454 sm_free(ProcListVec); 2455 } 2456 2457 /* XXX just use memset() to initialize this part? */ 2458 for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++) 2459 { 2460 npv[i].proc_pid = NO_PID; 2461 npv[i].proc_task = NULL; 2462 npv[i].proc_type = PROC_NONE; 2463 } 2464 i = ProcListSize; 2465 ProcListSize += PROC_LIST_SEG; 2466 ProcListVec = npv; 2467 } 2468 ProcListVec[i].proc_pid = pid; 2469 PSTRSET(ProcListVec[i].proc_task, task); 2470 ProcListVec[i].proc_type = type; 2471 ProcListVec[i].proc_count = count; 2472 ProcListVec[i].proc_other = other; 2473 2474 /* if process adding itself, it's not a child */ 2475 if (pid != CurrentPid) 2476 { 2477 SM_ASSERT(CurChildren < INT_MAX); 2478 CurChildren++; 2479 } 2480} 2481/* 2482** PROC_LIST_SET -- set pid task in process list 2483** 2484** Parameters: 2485** pid -- pid to set 2486** task -- task of pid 2487** 2488** Returns: 2489** none. 2490*/ 2491 2492void 2493proc_list_set(pid, task) 2494 pid_t pid; 2495 char *task; 2496{ 2497 int i; 2498 2499 for (i = 0; i < ProcListSize; i++) 2500 { 2501 if (ProcListVec[i].proc_pid == pid) 2502 { 2503 PSTRSET(ProcListVec[i].proc_task, task); 2504 break; 2505 } 2506 } 2507} 2508/* 2509** PROC_LIST_DROP -- drop pid from process list 2510** 2511** Parameters: 2512** pid -- pid to drop 2513** st -- process status 2514** other -- storage for proc_other (return). 2515** 2516** Returns: 2517** none. 2518** 2519** Side Effects: 2520** May decrease CurChildren, CurRunners, or 2521** set RestartRequest or ShutdownRequest. 2522** 2523** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 2524** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 2525** DOING. 2526*/ 2527 2528void 2529proc_list_drop(pid, st, other) 2530 pid_t pid; 2531 int st; 2532 int *other; 2533{ 2534 int i; 2535 int type = PROC_NONE; 2536 2537 for (i = 0; i < ProcListSize; i++) 2538 { 2539 if (ProcListVec[i].proc_pid == pid) 2540 { 2541 ProcListVec[i].proc_pid = NO_PID; 2542 type = ProcListVec[i].proc_type; 2543 if (other != NULL) 2544 *other = ProcListVec[i].proc_other; 2545 break; 2546 } 2547 } 2548 if (CurChildren > 0) 2549 CurChildren--; 2550 2551 2552 if (type == PROC_CONTROL && WIFEXITED(st)) 2553 { 2554 /* if so, see if we need to restart or shutdown */ 2555 if (WEXITSTATUS(st) == EX_RESTART) 2556 RestartRequest = "control socket"; 2557 else if (WEXITSTATUS(st) == EX_SHUTDOWN) 2558 ShutdownRequest = "control socket"; 2559 } 2560 else if (type == PROC_QUEUE_CHILD && !WIFSTOPPED(st) && 2561 ProcListVec[i].proc_other > -1) 2562 { 2563 /* restart this persistent runner */ 2564 mark_work_group_restart(ProcListVec[i].proc_other, st); 2565 } 2566 else if (type == PROC_QUEUE) 2567 CurRunners -= ProcListVec[i].proc_count; 2568} 2569/* 2570** PROC_LIST_CLEAR -- clear the process list 2571** 2572** Parameters: 2573** none. 2574** 2575** Returns: 2576** none. 2577** 2578** Side Effects: 2579** Sets CurChildren to zero. 2580*/ 2581 2582void 2583proc_list_clear() 2584{ 2585 int i; 2586 2587 /* start from 1 since 0 is the daemon itself */ 2588 for (i = 1; i < ProcListSize; i++) 2589 ProcListVec[i].proc_pid = NO_PID; 2590 CurChildren = 0; 2591} 2592/* 2593** PROC_LIST_PROBE -- probe processes in the list to see if they still exist 2594** 2595** Parameters: 2596** none 2597** 2598** Returns: 2599** none 2600** 2601** Side Effects: 2602** May decrease CurChildren. 2603*/ 2604 2605void 2606proc_list_probe() 2607{ 2608 int i; 2609 2610 /* start from 1 since 0 is the daemon itself */ 2611 for (i = 1; i < ProcListSize; i++) 2612 { 2613 if (ProcListVec[i].proc_pid == NO_PID) 2614 continue; 2615 if (kill(ProcListVec[i].proc_pid, 0) < 0) 2616 { 2617 if (LogLevel > 3) 2618 sm_syslog(LOG_DEBUG, CurEnv->e_id, 2619 "proc_list_probe: lost pid %d", 2620 (int) ProcListVec[i].proc_pid); 2621 ProcListVec[i].proc_pid = NO_PID; 2622 SM_FREE_CLR(ProcListVec[i].proc_task); 2623 CurChildren--; 2624 } 2625 } 2626 if (CurChildren < 0) 2627 CurChildren = 0; 2628} 2629 2630/* 2631** PROC_LIST_DISPLAY -- display the process list 2632** 2633** Parameters: 2634** out -- output file pointer 2635** prefix -- string to output in front of each line. 2636** 2637** Returns: 2638** none. 2639*/ 2640 2641void 2642proc_list_display(out, prefix) 2643 SM_FILE_T *out; 2644 char *prefix; 2645{ 2646 int i; 2647 2648 for (i = 0; i < ProcListSize; i++) 2649 { 2650 if (ProcListVec[i].proc_pid == NO_PID) 2651 continue; 2652 2653 (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "%s%d %s%s\n", 2654 prefix, 2655 (int) ProcListVec[i].proc_pid, 2656 ProcListVec[i].proc_task != NULL ? 2657 ProcListVec[i].proc_task : "(unknown)", 2658 (OpMode == MD_SMTP || 2659 OpMode == MD_DAEMON || 2660 OpMode == MD_ARPAFTP) ? "\r" : ""); 2661 } 2662} 2663 2664/* 2665** PROC_LIST_SIGNAL -- send a signal to a type of process in the list 2666** 2667** Parameters: 2668** type -- type of process to signal 2669** signal -- the type of signal to send 2670** 2671** Results: 2672** none. 2673** 2674** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 2675** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 2676** DOING. 2677*/ 2678 2679void 2680proc_list_signal(type, signal) 2681 int type; 2682 int signal; 2683{ 2684 int chldwasblocked; 2685 int alrmwasblocked; 2686 int i; 2687 pid_t mypid = getpid(); 2688 2689 /* block these signals so that we may signal cleanly */ 2690 chldwasblocked = sm_blocksignal(SIGCHLD); 2691 alrmwasblocked = sm_blocksignal(SIGALRM); 2692 2693 /* Find all processes of type and send signal */ 2694 for (i = 0; i < ProcListSize; i++) 2695 { 2696 if (ProcListVec[i].proc_pid == NO_PID || 2697 ProcListVec[i].proc_pid == mypid) 2698 continue; 2699 if (ProcListVec[i].proc_type != type) 2700 continue; 2701 (void) kill(ProcListVec[i].proc_pid, signal); 2702 } 2703 2704 /* restore the signals */ 2705 if (alrmwasblocked == 0) 2706 (void) sm_releasesignal(SIGALRM); 2707 if (chldwasblocked == 0) 2708 (void) sm_releasesignal(SIGCHLD); 2709} 2710