1 /* 2 * Copyright (c) 1998-2001 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 16 SM_RCSID("@(#)$Id: util.c,v 8.357 2001/11/28 19:19:27 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 34 char * 35 addquotes(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 84 bool 85 rfc822_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 146 bool 147 shorten_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 192 increment: 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 252 char * 253 find_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 317 int 318 check_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 344 void 345 truncate_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 391 char * 392 #if SM_HEAP_CHECK 393 xalloc_tagged(sz, file, line) 394 register int sz; 395 char *file; 396 int line; 397 #else /* SM_HEAP_CHECK */ 398 xalloc(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 436 char ** 437 copyplist(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 475 ADDRESS * 476 copyqueue(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 513 void 514 log_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 567 void 568 set_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 593 void 594 set_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 618 void 619 printav(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 645 void 646 xputs(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 823 void 824 makelower(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 854 void 855 fixcrlf(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 887 void 888 putline(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 ** 909 ** Returns: 910 ** none 911 ** 912 ** Side Effects: 913 ** output of l to mci->mci_out. 914 */ 915 916 void 917 putxline(l, len, mci, pxflags) 918 register char *l; 919 size_t len; 920 register MCI *mci; 921 int pxflags; 922 { 923 bool dead = false; 924 register char *p, *end; 925 int slop = 0; 926 927 /* strip out 0200 bits -- these can look like TELNET protocol */ 928 if (bitset(MCIF_7BIT, mci->mci_flags) || 929 bitset(PXLF_STRIP8BIT, pxflags)) 930 { 931 register char svchar; 932 933 for (p = l; (svchar = *p) != '\0'; ++p) 934 if (bitset(0200, svchar)) 935 *p = svchar &~ 0200; 936 } 937 938 end = l + len; 939 do 940 { 941 /* find the end of the line */ 942 p = memchr(l, '\n', end - l); 943 if (p == NULL) 944 p = end; 945 946 if (TrafficLogFile != NULL) 947 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 948 "%05d >>> ", (int) CurrentPid); 949 950 /* check for line overflow */ 951 while (mci->mci_mailer->m_linelimit > 0 && 952 (p - l + slop) > mci->mci_mailer->m_linelimit) 953 { 954 char *l_base = l; 955 register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1]; 956 957 if (l[0] == '.' && slop == 0 && 958 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 959 { 960 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 961 '.') == SM_IO_EOF) 962 dead = true; 963 else 964 { 965 /* record progress for DATA timeout */ 966 DataProgress = true; 967 } 968 if (TrafficLogFile != NULL) 969 (void) sm_io_putc(TrafficLogFile, 970 SM_TIME_DEFAULT, '.'); 971 } 972 else if (l[0] == 'F' && slop == 0 && 973 bitset(PXLF_MAPFROM, pxflags) && 974 strncmp(l, "From ", 5) == 0 && 975 bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 976 { 977 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 978 '>') == SM_IO_EOF) 979 dead = true; 980 else 981 { 982 /* record progress for DATA timeout */ 983 DataProgress = true; 984 } 985 if (TrafficLogFile != NULL) 986 (void) sm_io_putc(TrafficLogFile, 987 SM_TIME_DEFAULT, 988 '>'); 989 } 990 if (dead) 991 break; 992 993 while (l < q) 994 { 995 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 996 (unsigned char) *l++) == SM_IO_EOF) 997 { 998 dead = true; 999 break; 1000 } 1001 else 1002 { 1003 /* record progress for DATA timeout */ 1004 DataProgress = true; 1005 } 1006 } 1007 if (dead) 1008 break; 1009 1010 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '!') == 1011 SM_IO_EOF || 1012 sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 1013 mci->mci_mailer->m_eol) == 1014 SM_IO_EOF || 1015 sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, ' ') == 1016 SM_IO_EOF) 1017 { 1018 dead = true; 1019 break; 1020 } 1021 else 1022 { 1023 /* record progress for DATA timeout */ 1024 DataProgress = true; 1025 } 1026 if (TrafficLogFile != NULL) 1027 { 1028 for (l = l_base; l < q; l++) 1029 (void) sm_io_putc(TrafficLogFile, 1030 SM_TIME_DEFAULT, 1031 (unsigned char)*l); 1032 (void) sm_io_fprintf(TrafficLogFile, 1033 SM_TIME_DEFAULT, 1034 "!\n%05d >>> ", 1035 (int) CurrentPid); 1036 } 1037 slop = 1; 1038 } 1039 1040 if (dead) 1041 break; 1042 1043 /* output last part */ 1044 if (l[0] == '.' && slop == 0 && 1045 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 1046 { 1047 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '.') == 1048 SM_IO_EOF) 1049 break; 1050 else 1051 { 1052 /* record progress for DATA timeout */ 1053 DataProgress = true; 1054 } 1055 if (TrafficLogFile != NULL) 1056 (void) sm_io_putc(TrafficLogFile, 1057 SM_TIME_DEFAULT, '.'); 1058 } 1059 else if (l[0] == 'F' && slop == 0 && 1060 bitset(PXLF_MAPFROM, pxflags) && 1061 strncmp(l, "From ", 5) == 0 && 1062 bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 1063 { 1064 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '>') == 1065 SM_IO_EOF) 1066 break; 1067 else 1068 { 1069 /* record progress for DATA timeout */ 1070 DataProgress = true; 1071 } 1072 if (TrafficLogFile != NULL) 1073 (void) sm_io_putc(TrafficLogFile, 1074 SM_TIME_DEFAULT, '>'); 1075 } 1076 for ( ; l < p; ++l) 1077 { 1078 if (TrafficLogFile != NULL) 1079 (void) sm_io_putc(TrafficLogFile, 1080 SM_TIME_DEFAULT, 1081 (unsigned char)*l); 1082 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1083 (unsigned char) *l) == SM_IO_EOF) 1084 { 1085 dead = true; 1086 break; 1087 } 1088 else 1089 { 1090 /* record progress for DATA timeout */ 1091 DataProgress = true; 1092 } 1093 } 1094 if (dead) 1095 break; 1096 1097 if (TrafficLogFile != NULL) 1098 (void) sm_io_putc(TrafficLogFile, SM_TIME_DEFAULT, 1099 '\n'); 1100 if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 1101 mci->mci_mailer->m_eol) == SM_IO_EOF) 1102 break; 1103 else 1104 { 1105 /* record progress for DATA timeout */ 1106 DataProgress = true; 1107 } 1108 if (l < end && *l == '\n') 1109 { 1110 if (*++l != ' ' && *l != '\t' && *l != '\0' && 1111 bitset(PXLF_HEADER, pxflags)) 1112 { 1113 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1114 ' ') == SM_IO_EOF) 1115 break; 1116 else 1117 { 1118 /* record progress for DATA timeout */ 1119 DataProgress = true; 1120 } 1121 1122 if (TrafficLogFile != NULL) 1123 (void) sm_io_putc(TrafficLogFile, 1124 SM_TIME_DEFAULT, ' '); 1125 } 1126 } 1127 1128 /* record progress for DATA timeout */ 1129 DataProgress = true; 1130 } while (l < end); 1131 } 1132 /* 1133 ** XUNLINK -- unlink a file, doing logging as appropriate. 1134 ** 1135 ** Parameters: 1136 ** f -- name of file to unlink. 1137 ** 1138 ** Returns: 1139 ** return value of unlink() 1140 ** 1141 ** Side Effects: 1142 ** f is unlinked. 1143 */ 1144 1145 int 1146 xunlink(f) 1147 char *f; 1148 { 1149 register int i; 1150 int save_errno; 1151 1152 if (LogLevel > 98) 1153 sm_syslog(LOG_DEBUG, CurEnv->e_id, "unlink %s", f); 1154 1155 i = unlink(f); 1156 save_errno = errno; 1157 if (i < 0 && LogLevel > 97) 1158 sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s: unlink-fail %d", 1159 f, errno); 1160 if (i >= 0) 1161 SYNC_DIR(f, false); 1162 errno = save_errno; 1163 return i; 1164 } 1165 /* 1166 ** SFGETS -- "safe" fgets -- times out and ignores random interrupts. 1167 ** 1168 ** Parameters: 1169 ** buf -- place to put the input line. 1170 ** siz -- size of buf. 1171 ** fp -- file to read from. 1172 ** timeout -- the timeout before error occurs. 1173 ** during -- what we are trying to read (for error messages). 1174 ** 1175 ** Returns: 1176 ** NULL on error (including timeout). This may also leave 1177 ** buf containing a null string. 1178 ** buf otherwise. 1179 */ 1180 1181 1182 char * 1183 sfgets(buf, siz, fp, timeout, during) 1184 char *buf; 1185 int siz; 1186 SM_FILE_T *fp; 1187 time_t timeout; 1188 char *during; 1189 { 1190 register char *p; 1191 int save_errno; 1192 int io_timeout; 1193 1194 SM_REQUIRE(siz > 0); 1195 SM_REQUIRE(buf != NULL); 1196 1197 if (fp == NULL) 1198 { 1199 buf[0] = '\0'; 1200 errno = EBADF; 1201 return NULL; 1202 } 1203 1204 /* try to read */ 1205 p = NULL; 1206 errno = 0; 1207 1208 /* convert the timeout to sm_io notation */ 1209 io_timeout = (timeout <= 0) ? SM_TIME_DEFAULT : timeout * 1000; 1210 while (!sm_io_eof(fp) && !sm_io_error(fp)) 1211 { 1212 errno = 0; 1213 p = sm_io_fgets(fp, io_timeout, buf, siz); 1214 if (p == NULL && errno == EAGAIN) 1215 { 1216 /* The sm_io_fgets() call timedout */ 1217 if (LogLevel > 1) 1218 sm_syslog(LOG_NOTICE, CurEnv->e_id, 1219 "timeout waiting for input from %.100s during %s", 1220 CURHOSTNAME, 1221 during); 1222 buf[0] = '\0'; 1223 #if XDEBUG 1224 checkfd012(during); 1225 #endif /* XDEBUG */ 1226 if (TrafficLogFile != NULL) 1227 (void) sm_io_fprintf(TrafficLogFile, 1228 SM_TIME_DEFAULT, 1229 "%05d <<< [TIMEOUT]\n", 1230 (int) CurrentPid); 1231 errno = ETIMEDOUT; 1232 return NULL; 1233 } 1234 if (p != NULL || errno != EINTR) 1235 break; 1236 (void) sm_io_clearerr(fp); 1237 } 1238 save_errno = errno; 1239 1240 /* clean up the books and exit */ 1241 LineNumber++; 1242 if (p == NULL) 1243 { 1244 buf[0] = '\0'; 1245 if (TrafficLogFile != NULL) 1246 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 1247 "%05d <<< [EOF]\n", 1248 (int) CurrentPid); 1249 errno = save_errno; 1250 return NULL; 1251 } 1252 if (TrafficLogFile != NULL) 1253 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 1254 "%05d <<< %s", (int) CurrentPid, buf); 1255 if (SevenBitInput) 1256 { 1257 for (p = buf; *p != '\0'; p++) 1258 *p &= ~0200; 1259 } 1260 else if (!HasEightBits) 1261 { 1262 for (p = buf; *p != '\0'; p++) 1263 { 1264 if (bitset(0200, *p)) 1265 { 1266 HasEightBits = true; 1267 break; 1268 } 1269 } 1270 } 1271 return buf; 1272 } 1273 /* 1274 ** FGETFOLDED -- like fgets, but knows about folded lines. 1275 ** 1276 ** Parameters: 1277 ** buf -- place to put result. 1278 ** n -- bytes available. 1279 ** f -- file to read from. 1280 ** 1281 ** Returns: 1282 ** input line(s) on success, NULL on error or SM_IO_EOF. 1283 ** This will normally be buf -- unless the line is too 1284 ** long, when it will be sm_malloc_x()ed. 1285 ** 1286 ** Side Effects: 1287 ** buf gets lines from f, with continuation lines (lines 1288 ** with leading white space) appended. CRLF's are mapped 1289 ** into single newlines. Any trailing NL is stripped. 1290 */ 1291 1292 char * 1293 fgetfolded(buf, n, f) 1294 char *buf; 1295 register int n; 1296 SM_FILE_T *f; 1297 { 1298 register char *p = buf; 1299 char *bp = buf; 1300 register int i; 1301 1302 SM_REQUIRE(n > 0); 1303 SM_REQUIRE(buf != NULL); 1304 if (f == NULL) 1305 { 1306 buf[0] = '\0'; 1307 errno = EBADF; 1308 return NULL; 1309 } 1310 1311 n--; 1312 while ((i = sm_io_getc(f, SM_TIME_DEFAULT)) != SM_IO_EOF) 1313 { 1314 if (i == '\r') 1315 { 1316 i = sm_io_getc(f, SM_TIME_DEFAULT); 1317 if (i != '\n') 1318 { 1319 if (i != SM_IO_EOF) 1320 (void) sm_io_ungetc(f, SM_TIME_DEFAULT, 1321 i); 1322 i = '\r'; 1323 } 1324 } 1325 if (--n <= 0) 1326 { 1327 /* allocate new space */ 1328 char *nbp; 1329 int nn; 1330 1331 nn = (p - bp); 1332 if (nn < MEMCHUNKSIZE) 1333 nn *= 2; 1334 else 1335 nn += MEMCHUNKSIZE; 1336 nbp = sm_malloc_x(nn); 1337 memmove(nbp, bp, p - bp); 1338 p = &nbp[p - bp]; 1339 if (bp != buf) 1340 sm_free(bp); 1341 bp = nbp; 1342 n = nn - (p - bp); 1343 } 1344 *p++ = i; 1345 if (i == '\n') 1346 { 1347 LineNumber++; 1348 i = sm_io_getc(f, SM_TIME_DEFAULT); 1349 if (i != SM_IO_EOF) 1350 (void) sm_io_ungetc(f, SM_TIME_DEFAULT, i); 1351 if (i != ' ' && i != '\t') 1352 break; 1353 } 1354 } 1355 if (p == bp) 1356 return NULL; 1357 if (p[-1] == '\n') 1358 p--; 1359 *p = '\0'; 1360 return bp; 1361 } 1362 /* 1363 ** CURTIME -- return current time. 1364 ** 1365 ** Parameters: 1366 ** none. 1367 ** 1368 ** Returns: 1369 ** the current time. 1370 */ 1371 1372 time_t 1373 curtime() 1374 { 1375 auto time_t t; 1376 1377 (void) time(&t); 1378 return t; 1379 } 1380 /* 1381 ** ATOBOOL -- convert a string representation to boolean. 1382 ** 1383 ** Defaults to false 1384 ** 1385 ** Parameters: 1386 ** s -- string to convert. Takes "tTyY", empty, and NULL as true, 1387 ** others as false. 1388 ** 1389 ** Returns: 1390 ** A boolean representation of the string. 1391 */ 1392 1393 bool 1394 atobool(s) 1395 register char *s; 1396 { 1397 if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL) 1398 return true; 1399 return false; 1400 } 1401 /* 1402 ** ATOOCT -- convert a string representation to octal. 1403 ** 1404 ** Parameters: 1405 ** s -- string to convert. 1406 ** 1407 ** Returns: 1408 ** An integer representing the string interpreted as an 1409 ** octal number. 1410 */ 1411 1412 int 1413 atooct(s) 1414 register char *s; 1415 { 1416 register int i = 0; 1417 1418 while (*s >= '0' && *s <= '7') 1419 i = (i << 3) | (*s++ - '0'); 1420 return i; 1421 } 1422 /* 1423 ** BITINTERSECT -- tell if two bitmaps intersect 1424 ** 1425 ** Parameters: 1426 ** a, b -- the bitmaps in question 1427 ** 1428 ** Returns: 1429 ** true if they have a non-null intersection 1430 ** false otherwise 1431 */ 1432 1433 bool 1434 bitintersect(a, b) 1435 BITMAP256 a; 1436 BITMAP256 b; 1437 { 1438 int i; 1439 1440 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1441 { 1442 if ((a[i] & b[i]) != 0) 1443 return true; 1444 } 1445 return false; 1446 } 1447 /* 1448 ** BITZEROP -- tell if a bitmap is all zero 1449 ** 1450 ** Parameters: 1451 ** map -- the bit map to check 1452 ** 1453 ** Returns: 1454 ** true if map is all zero. 1455 ** false if there are any bits set in map. 1456 */ 1457 1458 bool 1459 bitzerop(map) 1460 BITMAP256 map; 1461 { 1462 int i; 1463 1464 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1465 { 1466 if (map[i] != 0) 1467 return false; 1468 } 1469 return true; 1470 } 1471 /* 1472 ** STRCONTAINEDIN -- tell if one string is contained in another 1473 ** 1474 ** Parameters: 1475 ** icase -- ignore case? 1476 ** a -- possible substring. 1477 ** b -- possible superstring. 1478 ** 1479 ** Returns: 1480 ** true if a is contained in b (case insensitive). 1481 ** false otherwise. 1482 */ 1483 1484 bool 1485 strcontainedin(icase, a, b) 1486 bool icase; 1487 register char *a; 1488 register char *b; 1489 { 1490 int la; 1491 int lb; 1492 int c; 1493 1494 la = strlen(a); 1495 lb = strlen(b); 1496 c = *a; 1497 if (icase && isascii(c) && isupper(c)) 1498 c = tolower(c); 1499 for (; lb-- >= la; b++) 1500 { 1501 if (icase) 1502 { 1503 if (*b != c && 1504 isascii(*b) && isupper(*b) && tolower(*b) != c) 1505 continue; 1506 if (sm_strncasecmp(a, b, la) == 0) 1507 return true; 1508 } 1509 else 1510 { 1511 if (*b != c) 1512 continue; 1513 if (strncmp(a, b, la) == 0) 1514 return true; 1515 } 1516 } 1517 return false; 1518 } 1519 /* 1520 ** CHECKFD012 -- check low numbered file descriptors 1521 ** 1522 ** File descriptors 0, 1, and 2 should be open at all times. 1523 ** This routine verifies that, and fixes it if not true. 1524 ** 1525 ** Parameters: 1526 ** where -- a tag printed if the assertion failed 1527 ** 1528 ** Returns: 1529 ** none 1530 */ 1531 1532 void 1533 checkfd012(where) 1534 char *where; 1535 { 1536 #if XDEBUG 1537 register int i; 1538 1539 for (i = 0; i < 3; i++) 1540 fill_fd(i, where); 1541 #endif /* XDEBUG */ 1542 } 1543 /* 1544 ** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging 1545 ** 1546 ** Parameters: 1547 ** fd -- file descriptor to check. 1548 ** where -- tag to print on failure. 1549 ** 1550 ** Returns: 1551 ** none. 1552 */ 1553 1554 void 1555 checkfdopen(fd, where) 1556 int fd; 1557 char *where; 1558 { 1559 #if XDEBUG 1560 struct stat st; 1561 1562 if (fstat(fd, &st) < 0 && errno == EBADF) 1563 { 1564 syserr("checkfdopen(%d): %s not open as expected!", fd, where); 1565 printopenfds(true); 1566 } 1567 #endif /* XDEBUG */ 1568 } 1569 /* 1570 ** CHECKFDS -- check for new or missing file descriptors 1571 ** 1572 ** Parameters: 1573 ** where -- tag for printing. If null, take a base line. 1574 ** 1575 ** Returns: 1576 ** none 1577 ** 1578 ** Side Effects: 1579 ** If where is set, shows changes since the last call. 1580 */ 1581 1582 void 1583 checkfds(where) 1584 char *where; 1585 { 1586 int maxfd; 1587 register int fd; 1588 bool printhdr = true; 1589 int save_errno = errno; 1590 static BITMAP256 baseline; 1591 extern int DtableSize; 1592 1593 if (DtableSize > BITMAPBITS) 1594 maxfd = BITMAPBITS; 1595 else 1596 maxfd = DtableSize; 1597 if (where == NULL) 1598 clrbitmap(baseline); 1599 1600 for (fd = 0; fd < maxfd; fd++) 1601 { 1602 struct stat stbuf; 1603 1604 if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP) 1605 { 1606 if (!bitnset(fd, baseline)) 1607 continue; 1608 clrbitn(fd, baseline); 1609 } 1610 else if (!bitnset(fd, baseline)) 1611 setbitn(fd, baseline); 1612 else 1613 continue; 1614 1615 /* file state has changed */ 1616 if (where == NULL) 1617 continue; 1618 if (printhdr) 1619 { 1620 sm_syslog(LOG_DEBUG, CurEnv->e_id, 1621 "%s: changed fds:", 1622 where); 1623 printhdr = false; 1624 } 1625 dumpfd(fd, true, true); 1626 } 1627 errno = save_errno; 1628 } 1629 /* 1630 ** PRINTOPENFDS -- print the open file descriptors (for debugging) 1631 ** 1632 ** Parameters: 1633 ** logit -- if set, send output to syslog; otherwise 1634 ** print for debugging. 1635 ** 1636 ** Returns: 1637 ** none. 1638 */ 1639 1640 #if NETINET || NETINET6 1641 # include <arpa/inet.h> 1642 #endif /* NETINET || NETINET6 */ 1643 1644 void 1645 printopenfds(logit) 1646 bool logit; 1647 { 1648 register int fd; 1649 extern int DtableSize; 1650 1651 for (fd = 0; fd < DtableSize; fd++) 1652 dumpfd(fd, false, logit); 1653 } 1654 /* 1655 ** DUMPFD -- dump a file descriptor 1656 ** 1657 ** Parameters: 1658 ** fd -- the file descriptor to dump. 1659 ** printclosed -- if set, print a notification even if 1660 ** it is closed; otherwise print nothing. 1661 ** logit -- if set, send output to syslog instead of stdout. 1662 ** 1663 ** Returns: 1664 ** none. 1665 */ 1666 1667 void 1668 dumpfd(fd, printclosed, logit) 1669 int fd; 1670 bool printclosed; 1671 bool logit; 1672 { 1673 register char *p; 1674 char *hp; 1675 #ifdef S_IFSOCK 1676 SOCKADDR sa; 1677 #endif /* S_IFSOCK */ 1678 auto SOCKADDR_LEN_T slen; 1679 int i; 1680 #if STAT64 > 0 1681 struct stat64 st; 1682 #else /* STAT64 > 0 */ 1683 struct stat st; 1684 #endif /* STAT64 > 0 */ 1685 char buf[200]; 1686 1687 p = buf; 1688 (void) sm_snprintf(p, SPACELEFT(buf, p), "%3d: ", fd); 1689 p += strlen(p); 1690 1691 if ( 1692 #if STAT64 > 0 1693 fstat64(fd, &st) 1694 #else /* STAT64 > 0 */ 1695 fstat(fd, &st) 1696 #endif /* STAT64 > 0 */ 1697 < 0) 1698 { 1699 if (errno != EBADF) 1700 { 1701 (void) sm_snprintf(p, SPACELEFT(buf, p), 1702 "CANNOT STAT (%s)", 1703 sm_errstring(errno)); 1704 goto printit; 1705 } 1706 else if (printclosed) 1707 { 1708 (void) sm_snprintf(p, SPACELEFT(buf, p), "CLOSED"); 1709 goto printit; 1710 } 1711 return; 1712 } 1713 1714 i = fcntl(fd, F_GETFL, NULL); 1715 if (i != -1) 1716 { 1717 (void) sm_snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i); 1718 p += strlen(p); 1719 } 1720 1721 (void) sm_snprintf(p, SPACELEFT(buf, p), "mode=%o: ", 1722 (int) st.st_mode); 1723 p += strlen(p); 1724 switch (st.st_mode & S_IFMT) 1725 { 1726 #ifdef S_IFSOCK 1727 case S_IFSOCK: 1728 (void) sm_snprintf(p, SPACELEFT(buf, p), "SOCK "); 1729 p += strlen(p); 1730 memset(&sa, '\0', sizeof sa); 1731 slen = sizeof sa; 1732 if (getsockname(fd, &sa.sa, &slen) < 0) 1733 (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)", 1734 sm_errstring(errno)); 1735 else 1736 { 1737 hp = hostnamebyanyaddr(&sa); 1738 if (hp == NULL) 1739 { 1740 /* EMPTY */ 1741 /* do nothing */ 1742 } 1743 # if NETINET 1744 else if (sa.sa.sa_family == AF_INET) 1745 (void) sm_snprintf(p, SPACELEFT(buf, p), 1746 "%s/%d", hp, ntohs(sa.sin.sin_port)); 1747 # endif /* NETINET */ 1748 # if NETINET6 1749 else if (sa.sa.sa_family == AF_INET6) 1750 (void) sm_snprintf(p, SPACELEFT(buf, p), 1751 "%s/%d", hp, ntohs(sa.sin6.sin6_port)); 1752 # endif /* NETINET6 */ 1753 else 1754 (void) sm_snprintf(p, SPACELEFT(buf, p), 1755 "%s", hp); 1756 } 1757 p += strlen(p); 1758 (void) sm_snprintf(p, SPACELEFT(buf, p), "->"); 1759 p += strlen(p); 1760 slen = sizeof sa; 1761 if (getpeername(fd, &sa.sa, &slen) < 0) 1762 (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)", 1763 sm_errstring(errno)); 1764 else 1765 { 1766 hp = hostnamebyanyaddr(&sa); 1767 if (hp == NULL) 1768 { 1769 /* EMPTY */ 1770 /* do nothing */ 1771 } 1772 # if NETINET 1773 else if (sa.sa.sa_family == AF_INET) 1774 (void) sm_snprintf(p, SPACELEFT(buf, p), 1775 "%s/%d", hp, ntohs(sa.sin.sin_port)); 1776 # endif /* NETINET */ 1777 # if NETINET6 1778 else if (sa.sa.sa_family == AF_INET6) 1779 (void) sm_snprintf(p, SPACELEFT(buf, p), 1780 "%s/%d", hp, ntohs(sa.sin6.sin6_port)); 1781 # endif /* NETINET6 */ 1782 else 1783 (void) sm_snprintf(p, SPACELEFT(buf, p), 1784 "%s", hp); 1785 } 1786 break; 1787 #endif /* S_IFSOCK */ 1788 1789 case S_IFCHR: 1790 (void) sm_snprintf(p, SPACELEFT(buf, p), "CHR: "); 1791 p += strlen(p); 1792 goto defprint; 1793 1794 #ifdef S_IFBLK 1795 case S_IFBLK: 1796 (void) sm_snprintf(p, SPACELEFT(buf, p), "BLK: "); 1797 p += strlen(p); 1798 goto defprint; 1799 #endif /* S_IFBLK */ 1800 1801 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) 1802 case S_IFIFO: 1803 (void) sm_snprintf(p, SPACELEFT(buf, p), "FIFO: "); 1804 p += strlen(p); 1805 goto defprint; 1806 #endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */ 1807 1808 #ifdef S_IFDIR 1809 case S_IFDIR: 1810 (void) sm_snprintf(p, SPACELEFT(buf, p), "DIR: "); 1811 p += strlen(p); 1812 goto defprint; 1813 #endif /* S_IFDIR */ 1814 1815 #ifdef S_IFLNK 1816 case S_IFLNK: 1817 (void) sm_snprintf(p, SPACELEFT(buf, p), "LNK: "); 1818 p += strlen(p); 1819 goto defprint; 1820 #endif /* S_IFLNK */ 1821 1822 default: 1823 defprint: 1824 (void) sm_snprintf(p, SPACELEFT(buf, p), 1825 "dev=%d/%d, ino=%llu, nlink=%d, u/gid=%d/%d, ", 1826 major(st.st_dev), minor(st.st_dev), 1827 (ULONGLONG_T) st.st_ino, 1828 (int) st.st_nlink, (int) st.st_uid, 1829 (int) st.st_gid); 1830 p += strlen(p); 1831 (void) sm_snprintf(p, SPACELEFT(buf, p), "size=%llu", 1832 (ULONGLONG_T) st.st_size); 1833 break; 1834 } 1835 1836 printit: 1837 if (logit) 1838 sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL, 1839 "%.800s", buf); 1840 else 1841 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", buf); 1842 } 1843 /* 1844 ** SHORTEN_HOSTNAME -- strip local domain information off of hostname. 1845 ** 1846 ** Parameters: 1847 ** host -- the host to shorten (stripped in place). 1848 ** 1849 ** Returns: 1850 ** place where string was truncated, NULL if not truncated. 1851 */ 1852 1853 char * 1854 shorten_hostname(host) 1855 char host[]; 1856 { 1857 register char *p; 1858 char *mydom; 1859 int i; 1860 bool canon = false; 1861 1862 /* strip off final dot */ 1863 i = strlen(host); 1864 p = &host[(i == 0) ? 0 : i - 1]; 1865 if (*p == '.') 1866 { 1867 *p = '\0'; 1868 canon = true; 1869 } 1870 1871 /* see if there is any domain at all -- if not, we are done */ 1872 p = strchr(host, '.'); 1873 if (p == NULL) 1874 return NULL; 1875 1876 /* yes, we have a domain -- see if it looks like us */ 1877 mydom = macvalue('m', CurEnv); 1878 if (mydom == NULL) 1879 mydom = ""; 1880 i = strlen(++p); 1881 if ((canon ? sm_strcasecmp(p, mydom) 1882 : sm_strncasecmp(p, mydom, i)) == 0 && 1883 (mydom[i] == '.' || mydom[i] == '\0')) 1884 { 1885 *--p = '\0'; 1886 return p; 1887 } 1888 return NULL; 1889 } 1890 /* 1891 ** PROG_OPEN -- open a program for reading 1892 ** 1893 ** Parameters: 1894 ** argv -- the argument list. 1895 ** pfd -- pointer to a place to store the file descriptor. 1896 ** e -- the current envelope. 1897 ** 1898 ** Returns: 1899 ** pid of the process -- -1 if it failed. 1900 */ 1901 1902 pid_t 1903 prog_open(argv, pfd, e) 1904 char **argv; 1905 int *pfd; 1906 ENVELOPE *e; 1907 { 1908 pid_t pid; 1909 int i; 1910 int save_errno; 1911 int sff; 1912 int ret; 1913 int fdv[2]; 1914 char *p, *q; 1915 char buf[MAXLINE + 1]; 1916 extern int DtableSize; 1917 1918 if (pipe(fdv) < 0) 1919 { 1920 syserr("%s: cannot create pipe for stdout", argv[0]); 1921 return -1; 1922 } 1923 pid = fork(); 1924 if (pid < 0) 1925 { 1926 syserr("%s: cannot fork", argv[0]); 1927 (void) close(fdv[0]); 1928 (void) close(fdv[1]); 1929 return -1; 1930 } 1931 if (pid > 0) 1932 { 1933 /* parent */ 1934 (void) close(fdv[1]); 1935 *pfd = fdv[0]; 1936 return pid; 1937 } 1938 1939 /* Reset global flags */ 1940 RestartRequest = NULL; 1941 RestartWorkGroup = false; 1942 ShutdownRequest = NULL; 1943 PendingSignal = 0; 1944 CurrentPid = getpid(); 1945 1946 /* 1947 ** Initialize exception stack and default exception 1948 ** handler for child process. 1949 */ 1950 1951 sm_exc_newthread(fatal_error); 1952 1953 /* child -- close stdin */ 1954 (void) close(0); 1955 1956 /* stdout goes back to parent */ 1957 (void) close(fdv[0]); 1958 if (dup2(fdv[1], 1) < 0) 1959 { 1960 syserr("%s: cannot dup2 for stdout", argv[0]); 1961 _exit(EX_OSERR); 1962 } 1963 (void) close(fdv[1]); 1964 1965 /* stderr goes to transcript if available */ 1966 if (e->e_xfp != NULL) 1967 { 1968 int xfd; 1969 1970 xfd = sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL); 1971 if (xfd >= 0 && dup2(xfd, 2) < 0) 1972 { 1973 syserr("%s: cannot dup2 for stderr", argv[0]); 1974 _exit(EX_OSERR); 1975 } 1976 } 1977 1978 /* this process has no right to the queue file */ 1979 if (e->e_lockfp != NULL) 1980 (void) close(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL)); 1981 1982 /* chroot to the program mailer directory, if defined */ 1983 if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL) 1984 { 1985 expand(ProgMailer->m_rootdir, buf, sizeof buf, e); 1986 if (chroot(buf) < 0) 1987 { 1988 syserr("prog_open: cannot chroot(%s)", buf); 1989 exit(EX_TEMPFAIL); 1990 } 1991 if (chdir("/") < 0) 1992 { 1993 syserr("prog_open: cannot chdir(/)"); 1994 exit(EX_TEMPFAIL); 1995 } 1996 } 1997 1998 /* run as default user */ 1999 endpwent(); 2000 sm_mbdb_terminate(); 2001 if (setgid(DefGid) < 0 && geteuid() == 0) 2002 { 2003 syserr("prog_open: setgid(%ld) failed", (long) DefGid); 2004 exit(EX_TEMPFAIL); 2005 } 2006 if (setuid(DefUid) < 0 && geteuid() == 0) 2007 { 2008 syserr("prog_open: setuid(%ld) failed", (long) DefUid); 2009 exit(EX_TEMPFAIL); 2010 } 2011 2012 /* run in some directory */ 2013 if (ProgMailer != NULL) 2014 p = ProgMailer->m_execdir; 2015 else 2016 p = NULL; 2017 for (; p != NULL; p = q) 2018 { 2019 q = strchr(p, ':'); 2020 if (q != NULL) 2021 *q = '\0'; 2022 expand(p, buf, sizeof buf, e); 2023 if (q != NULL) 2024 *q++ = ':'; 2025 if (buf[0] != '\0' && chdir(buf) >= 0) 2026 break; 2027 } 2028 if (p == NULL) 2029 { 2030 /* backup directories */ 2031 if (chdir("/tmp") < 0) 2032 (void) chdir("/"); 2033 } 2034 2035 /* Check safety of program to be run */ 2036 sff = SFF_ROOTOK|SFF_EXECOK; 2037 if (!bitnset(DBS_RUNWRITABLEPROGRAM, DontBlameSendmail)) 2038 sff |= SFF_NOGWFILES|SFF_NOWWFILES; 2039 if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, DontBlameSendmail)) 2040 sff |= SFF_NOPATHCHECK; 2041 else 2042 sff |= SFF_SAFEDIRPATH; 2043 ret = safefile(argv[0], DefUid, DefGid, DefUser, sff, 0, NULL); 2044 if (ret != 0) 2045 sm_syslog(LOG_INFO, e->e_id, 2046 "Warning: prog_open: program %s unsafe: %s", 2047 argv[0], sm_errstring(ret)); 2048 2049 /* arrange for all the files to be closed */ 2050 for (i = 3; i < DtableSize; i++) 2051 { 2052 register int j; 2053 2054 if ((j = fcntl(i, F_GETFD, 0)) != -1) 2055 (void) fcntl(i, F_SETFD, j | FD_CLOEXEC); 2056 } 2057 2058 /* now exec the process */ 2059 (void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron); 2060 2061 /* woops! failed */ 2062 save_errno = errno; 2063 syserr("%s: cannot exec", argv[0]); 2064 if (transienterror(save_errno)) 2065 _exit(EX_OSERR); 2066 _exit(EX_CONFIG); 2067 return -1; /* avoid compiler warning on IRIX */ 2068 } 2069 /* 2070 ** GET_COLUMN -- look up a Column in a line buffer 2071 ** 2072 ** Parameters: 2073 ** line -- the raw text line to search. 2074 ** col -- the column number to fetch. 2075 ** delim -- the delimiter between columns. If null, 2076 ** use white space. 2077 ** buf -- the output buffer. 2078 ** buflen -- the length of buf. 2079 ** 2080 ** Returns: 2081 ** buf if successful. 2082 ** NULL otherwise. 2083 */ 2084 2085 char * 2086 get_column(line, col, delim, buf, buflen) 2087 char line[]; 2088 int col; 2089 int delim; 2090 char buf[]; 2091 int buflen; 2092 { 2093 char *p; 2094 char *begin, *end; 2095 int i; 2096 char delimbuf[4]; 2097 2098 if ((char) delim == '\0') 2099 (void) sm_strlcpy(delimbuf, "\n\t ", sizeof delimbuf); 2100 else 2101 { 2102 delimbuf[0] = (char) delim; 2103 delimbuf[1] = '\0'; 2104 } 2105 2106 p = line; 2107 if (*p == '\0') 2108 return NULL; /* line empty */ 2109 if (*p == (char) delim && col == 0) 2110 return NULL; /* first column empty */ 2111 2112 begin = line; 2113 2114 if (col == 0 && (char) delim == '\0') 2115 { 2116 while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 2117 begin++; 2118 } 2119 2120 for (i = 0; i < col; i++) 2121 { 2122 if ((begin = strpbrk(begin, delimbuf)) == NULL) 2123 return NULL; /* no such column */ 2124 begin++; 2125 if ((char) delim == '\0') 2126 { 2127 while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 2128 begin++; 2129 } 2130 } 2131 2132 end = strpbrk(begin, delimbuf); 2133 if (end == NULL) 2134 i = strlen(begin); 2135 else 2136 i = end - begin; 2137 if (i >= buflen) 2138 i = buflen - 1; 2139 (void) sm_strlcpy(buf, begin, i + 1); 2140 return buf; 2141 } 2142 /* 2143 ** CLEANSTRCPY -- copy string keeping out bogus characters 2144 ** 2145 ** Parameters: 2146 ** t -- "to" string. 2147 ** f -- "from" string. 2148 ** l -- length of space available in "to" string. 2149 ** 2150 ** Returns: 2151 ** none. 2152 */ 2153 2154 void 2155 cleanstrcpy(t, f, l) 2156 register char *t; 2157 register char *f; 2158 int l; 2159 { 2160 /* check for newlines and log if necessary */ 2161 (void) denlstring(f, true, true); 2162 2163 if (l <= 0) 2164 syserr("!cleanstrcpy: length == 0"); 2165 2166 l--; 2167 while (l > 0 && *f != '\0') 2168 { 2169 if (isascii(*f) && 2170 (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL)) 2171 { 2172 l--; 2173 *t++ = *f; 2174 } 2175 f++; 2176 } 2177 *t = '\0'; 2178 } 2179 /* 2180 ** DENLSTRING -- convert newlines in a string to spaces 2181 ** 2182 ** Parameters: 2183 ** s -- the input string 2184 ** strict -- if set, don't permit continuation lines. 2185 ** logattacks -- if set, log attempted attacks. 2186 ** 2187 ** Returns: 2188 ** A pointer to a version of the string with newlines 2189 ** mapped to spaces. This should be copied. 2190 */ 2191 2192 char * 2193 denlstring(s, strict, logattacks) 2194 char *s; 2195 bool strict; 2196 bool logattacks; 2197 { 2198 register char *p; 2199 int l; 2200 static char *bp = NULL; 2201 static int bl = 0; 2202 2203 p = s; 2204 while ((p = strchr(p, '\n')) != NULL) 2205 if (strict || (*++p != ' ' && *p != '\t')) 2206 break; 2207 if (p == NULL) 2208 return s; 2209 2210 l = strlen(s) + 1; 2211 if (bl < l) 2212 { 2213 /* allocate more space */ 2214 char *nbp = sm_pmalloc_x(l); 2215 2216 if (bp != NULL) 2217 sm_free(bp); 2218 bp = nbp; 2219 bl = l; 2220 } 2221 (void) sm_strlcpy(bp, s, l); 2222 for (p = bp; (p = strchr(p, '\n')) != NULL; ) 2223 *p++ = ' '; 2224 2225 if (logattacks) 2226 { 2227 sm_syslog(LOG_NOTICE, CurEnv->e_id, 2228 "POSSIBLE ATTACK from %.100s: newline in string \"%s\"", 2229 RealHostName == NULL ? "[UNKNOWN]" : RealHostName, 2230 shortenstring(bp, MAXSHORTSTR)); 2231 } 2232 2233 return bp; 2234 } 2235 /* 2236 ** STR2PRT -- convert "unprintable" characters in a string to \oct 2237 ** 2238 ** Parameters: 2239 ** s -- string to convert 2240 ** 2241 ** Returns: 2242 ** converted string. 2243 ** This is a static local buffer, string must be copied 2244 ** before this function is called again! 2245 */ 2246 2247 char * 2248 str2prt(s) 2249 char *s; 2250 { 2251 int l; 2252 char c, *h; 2253 bool ok; 2254 static int len = 0; 2255 static char *buf = NULL; 2256 2257 if (s == NULL) 2258 return NULL; 2259 ok = true; 2260 for (h = s, l = 1; *h != '\0'; h++, l++) 2261 { 2262 if (*h == '\\') 2263 { 2264 ++l; 2265 ok = false; 2266 } 2267 else if (!(isascii(*h) && isprint(*h))) 2268 { 2269 l += 3; 2270 ok = false; 2271 } 2272 } 2273 if (ok) 2274 return s; 2275 if (l > len) 2276 { 2277 char *nbuf = sm_pmalloc_x(l); 2278 2279 if (buf != NULL) 2280 sm_free(buf); 2281 len = l; 2282 buf = nbuf; 2283 } 2284 for (h = buf; *s != '\0' && l > 0; s++, l--) 2285 { 2286 c = *s; 2287 if (isascii(c) && isprint(c) && c != '\\') 2288 { 2289 *h++ = c; 2290 } 2291 else 2292 { 2293 *h++ = '\\'; 2294 --l; 2295 switch (c) 2296 { 2297 case '\\': 2298 *h++ = '\\'; 2299 break; 2300 case '\t': 2301 *h++ = 't'; 2302 break; 2303 case '\n': 2304 *h++ = 'n'; 2305 break; 2306 case '\r': 2307 *h++ = 'r'; 2308 break; 2309 default: 2310 (void) sm_snprintf(h, l, "%03o", (int) c); 2311 2312 /* 2313 ** XXX since l is unsigned this may 2314 ** wrap around if the calculation is screwed 2315 ** up... 2316 */ 2317 2318 l -= 2; 2319 h += 3; 2320 break; 2321 } 2322 } 2323 } 2324 *h = '\0'; 2325 buf[len - 1] = '\0'; 2326 return buf; 2327 } 2328 /* 2329 ** PATH_IS_DIR -- check to see if file exists and is a directory. 2330 ** 2331 ** There are some additional checks for security violations in 2332 ** here. This routine is intended to be used for the host status 2333 ** support. 2334 ** 2335 ** Parameters: 2336 ** pathname -- pathname to check for directory-ness. 2337 ** createflag -- if set, create directory if needed. 2338 ** 2339 ** Returns: 2340 ** true -- if the indicated pathname is a directory 2341 ** false -- otherwise 2342 */ 2343 2344 int 2345 path_is_dir(pathname, createflag) 2346 char *pathname; 2347 bool createflag; 2348 { 2349 struct stat statbuf; 2350 2351 #if HASLSTAT 2352 if (lstat(pathname, &statbuf) < 0) 2353 #else /* HASLSTAT */ 2354 if (stat(pathname, &statbuf) < 0) 2355 #endif /* HASLSTAT */ 2356 { 2357 if (errno != ENOENT || !createflag) 2358 return false; 2359 if (mkdir(pathname, 0755) < 0) 2360 return false; 2361 return true; 2362 } 2363 if (!S_ISDIR(statbuf.st_mode)) 2364 { 2365 errno = ENOTDIR; 2366 return false; 2367 } 2368 2369 /* security: don't allow writable directories */ 2370 if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode)) 2371 { 2372 errno = EACCES; 2373 return false; 2374 } 2375 return true; 2376 } 2377 /* 2378 ** PROC_LIST_ADD -- add process id to list of our children 2379 ** 2380 ** Parameters: 2381 ** pid -- pid to add to list. 2382 ** task -- task of pid. 2383 ** type -- type of process. 2384 ** count -- number of processes. 2385 ** other -- other information for this type. 2386 ** 2387 ** Returns: 2388 ** none 2389 ** 2390 ** Side Effects: 2391 ** May increase CurChildren. May grow ProcList. 2392 */ 2393 2394 typedef struct procs PROCS_T; 2395 2396 struct procs 2397 { 2398 pid_t proc_pid; 2399 char *proc_task; 2400 int proc_type; 2401 int proc_count; 2402 int proc_other; 2403 }; 2404 2405 static PROCS_T *volatile ProcListVec = NULL; 2406 static int ProcListSize = 0; 2407 2408 void 2409 proc_list_add(pid, task, type, count, other) 2410 pid_t pid; 2411 char *task; 2412 int type; 2413 int count; 2414 int other; 2415 { 2416 int i; 2417 2418 for (i = 0; i < ProcListSize; i++) 2419 { 2420 if (ProcListVec[i].proc_pid == NO_PID) 2421 break; 2422 } 2423 if (i >= ProcListSize) 2424 { 2425 /* probe the existing vector to avoid growing infinitely */ 2426 proc_list_probe(); 2427 2428 /* now scan again */ 2429 for (i = 0; i < ProcListSize; i++) 2430 { 2431 if (ProcListVec[i].proc_pid == NO_PID) 2432 break; 2433 } 2434 } 2435 if (i >= ProcListSize) 2436 { 2437 /* grow process list */ 2438 PROCS_T *npv; 2439 2440 SM_ASSERT(ProcListSize < INT_MAX - PROC_LIST_SEG); 2441 npv = (PROCS_T *) sm_pmalloc_x((sizeof *npv) * 2442 (ProcListSize + PROC_LIST_SEG)); 2443 if (ProcListSize > 0) 2444 { 2445 memmove(npv, ProcListVec, 2446 ProcListSize * sizeof (PROCS_T)); 2447 sm_free(ProcListVec); 2448 } 2449 2450 /* XXX just use memset() to initialize this part? */ 2451 for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++) 2452 { 2453 npv[i].proc_pid = NO_PID; 2454 npv[i].proc_task = NULL; 2455 npv[i].proc_type = PROC_NONE; 2456 } 2457 i = ProcListSize; 2458 ProcListSize += PROC_LIST_SEG; 2459 ProcListVec = npv; 2460 } 2461 ProcListVec[i].proc_pid = pid; 2462 PSTRSET(ProcListVec[i].proc_task, task); 2463 ProcListVec[i].proc_type = type; 2464 ProcListVec[i].proc_count = count; 2465 ProcListVec[i].proc_other = other; 2466 2467 /* if process adding itself, it's not a child */ 2468 if (pid != CurrentPid) 2469 { 2470 SM_ASSERT(CurChildren < INT_MAX); 2471 CurChildren++; 2472 } 2473 } 2474 /* 2475 ** PROC_LIST_SET -- set pid task in process list 2476 ** 2477 ** Parameters: 2478 ** pid -- pid to set 2479 ** task -- task of pid 2480 ** 2481 ** Returns: 2482 ** none. 2483 */ 2484 2485 void 2486 proc_list_set(pid, task) 2487 pid_t pid; 2488 char *task; 2489 { 2490 int i; 2491 2492 for (i = 0; i < ProcListSize; i++) 2493 { 2494 if (ProcListVec[i].proc_pid == pid) 2495 { 2496 PSTRSET(ProcListVec[i].proc_task, task); 2497 break; 2498 } 2499 } 2500 } 2501 /* 2502 ** PROC_LIST_DROP -- drop pid from process list 2503 ** 2504 ** Parameters: 2505 ** pid -- pid to drop 2506 ** st -- process status 2507 ** other -- storage for proc_other (return). 2508 ** 2509 ** Returns: 2510 ** none. 2511 ** 2512 ** Side Effects: 2513 ** May decrease CurChildren, CurRunners, or 2514 ** set RestartRequest or ShutdownRequest. 2515 ** 2516 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 2517 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 2518 ** DOING. 2519 */ 2520 2521 void 2522 proc_list_drop(pid, st, other) 2523 pid_t pid; 2524 int st; 2525 int *other; 2526 { 2527 int i; 2528 int type = PROC_NONE; 2529 2530 for (i = 0; i < ProcListSize; i++) 2531 { 2532 if (ProcListVec[i].proc_pid == pid) 2533 { 2534 ProcListVec[i].proc_pid = NO_PID; 2535 type = ProcListVec[i].proc_type; 2536 if (other != NULL) 2537 *other = ProcListVec[i].proc_other; 2538 break; 2539 } 2540 } 2541 if (CurChildren > 0) 2542 CurChildren--; 2543 2544 2545 if (type == PROC_CONTROL && WIFEXITED(st)) 2546 { 2547 /* if so, see if we need to restart or shutdown */ 2548 if (WEXITSTATUS(st) == EX_RESTART) 2549 RestartRequest = "control socket"; 2550 else if (WEXITSTATUS(st) == EX_SHUTDOWN) 2551 ShutdownRequest = "control socket"; 2552 } 2553 else if (type == PROC_QUEUE_CHILD && !WIFSTOPPED(st) && 2554 ProcListVec[i].proc_other > -1) 2555 { 2556 /* restart this persistent runner */ 2557 mark_work_group_restart(ProcListVec[i].proc_other, st); 2558 } 2559 else if (type == PROC_QUEUE) 2560 CurRunners -= ProcListVec[i].proc_count; 2561 } 2562 /* 2563 ** PROC_LIST_CLEAR -- clear the process list 2564 ** 2565 ** Parameters: 2566 ** none. 2567 ** 2568 ** Returns: 2569 ** none. 2570 ** 2571 ** Side Effects: 2572 ** Sets CurChildren to zero. 2573 */ 2574 2575 void 2576 proc_list_clear() 2577 { 2578 int i; 2579 2580 /* start from 1 since 0 is the daemon itself */ 2581 for (i = 1; i < ProcListSize; i++) 2582 ProcListVec[i].proc_pid = NO_PID; 2583 CurChildren = 0; 2584 } 2585 /* 2586 ** PROC_LIST_PROBE -- probe processes in the list to see if they still exist 2587 ** 2588 ** Parameters: 2589 ** none 2590 ** 2591 ** Returns: 2592 ** none 2593 ** 2594 ** Side Effects: 2595 ** May decrease CurChildren. 2596 */ 2597 2598 void 2599 proc_list_probe() 2600 { 2601 int i; 2602 2603 /* start from 1 since 0 is the daemon itself */ 2604 for (i = 1; i < ProcListSize; i++) 2605 { 2606 if (ProcListVec[i].proc_pid == NO_PID) 2607 continue; 2608 if (kill(ProcListVec[i].proc_pid, 0) < 0) 2609 { 2610 if (LogLevel > 3) 2611 sm_syslog(LOG_DEBUG, CurEnv->e_id, 2612 "proc_list_probe: lost pid %d", 2613 (int) ProcListVec[i].proc_pid); 2614 ProcListVec[i].proc_pid = NO_PID; 2615 SM_FREE_CLR(ProcListVec[i].proc_task); 2616 CurChildren--; 2617 } 2618 } 2619 if (CurChildren < 0) 2620 CurChildren = 0; 2621 } 2622 2623 /* 2624 ** PROC_LIST_DISPLAY -- display the process list 2625 ** 2626 ** Parameters: 2627 ** out -- output file pointer 2628 ** prefix -- string to output in front of each line. 2629 ** 2630 ** Returns: 2631 ** none. 2632 */ 2633 2634 void 2635 proc_list_display(out, prefix) 2636 SM_FILE_T *out; 2637 char *prefix; 2638 { 2639 int i; 2640 2641 for (i = 0; i < ProcListSize; i++) 2642 { 2643 if (ProcListVec[i].proc_pid == NO_PID) 2644 continue; 2645 2646 (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "%s%d %s%s\n", 2647 prefix, 2648 (int) ProcListVec[i].proc_pid, 2649 ProcListVec[i].proc_task != NULL ? 2650 ProcListVec[i].proc_task : "(unknown)", 2651 (OpMode == MD_SMTP || 2652 OpMode == MD_DAEMON || 2653 OpMode == MD_ARPAFTP) ? "\r" : ""); 2654 } 2655 } 2656 2657 /* 2658 ** PROC_LIST_SIGNAL -- send a signal to a type of process in the list 2659 ** 2660 ** Parameters: 2661 ** type -- type of process to signal 2662 ** signal -- the type of signal to send 2663 ** 2664 ** Results: 2665 ** none. 2666 ** 2667 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 2668 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 2669 ** DOING. 2670 */ 2671 2672 void 2673 proc_list_signal(type, signal) 2674 int type; 2675 int signal; 2676 { 2677 int chldwasblocked; 2678 int alrmwasblocked; 2679 int i; 2680 pid_t mypid = getpid(); 2681 2682 /* block these signals so that we may signal cleanly */ 2683 chldwasblocked = sm_blocksignal(SIGCHLD); 2684 alrmwasblocked = sm_blocksignal(SIGALRM); 2685 2686 /* Find all processes of type and send signal */ 2687 for (i = 0; i < ProcListSize; i++) 2688 { 2689 if (ProcListVec[i].proc_pid == NO_PID || 2690 ProcListVec[i].proc_pid == mypid) 2691 continue; 2692 if (ProcListVec[i].proc_type != type) 2693 continue; 2694 (void) kill(ProcListVec[i].proc_pid, signal); 2695 } 2696 2697 /* restore the signals */ 2698 if (alrmwasblocked == 0) 2699 (void) sm_releasesignal(SIGALRM); 2700 if (chldwasblocked == 0) 2701 (void) sm_releasesignal(SIGCHLD); 2702 } 2703