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