util.c revision 98841
138032Speter/* 294334Sgshapiro * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1988, 1993 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 1238032Speter */ 1338032Speter 1464562Sgshapiro#include <sendmail.h> 1564562Sgshapiro 1698841SgshapiroSM_RCSID("@(#)$Id: util.c,v 8.363.2.1 2002/06/21 20:25:25 ca Exp $") 1764562Sgshapiro 1890792Sgshapiro#include <sysexits.h> 1990792Sgshapiro#include <sm/xtrap.h> 2064562Sgshapiro 2190792Sgshapiro/* 2238032Speter** ADDQUOTES -- Adds quotes & quote bits to a string. 2338032Speter** 2490792Sgshapiro** Runs through a string and adds backslashes and quote bits. 2538032Speter** 2638032Speter** Parameters: 2738032Speter** s -- the string to modify. 2890792Sgshapiro** rpool -- resource pool from which to allocate result 2938032Speter** 3038032Speter** Returns: 3138032Speter** pointer to quoted string. 3238032Speter*/ 3338032Speter 3438032Speterchar * 3590792Sgshapiroaddquotes(s, rpool) 3638032Speter char *s; 3790792Sgshapiro SM_RPOOL_T *rpool; 3838032Speter{ 3938032Speter int len = 0; 4038032Speter char c; 4138032Speter char *p = s, *q, *r; 4238032Speter 4338032Speter if (s == NULL) 4438032Speter return NULL; 4538032Speter 4638032Speter /* Find length of quoted string */ 4738032Speter while ((c = *p++) != '\0') 4838032Speter { 4938032Speter len++; 5038032Speter if (c == '\\' || c == '"') 5138032Speter len++; 5238032Speter } 5364562Sgshapiro 5490792Sgshapiro q = r = sm_rpool_malloc_x(rpool, len + 3); 5538032Speter p = s; 5638032Speter 5738032Speter /* add leading quote */ 5838032Speter *q++ = '"'; 5938032Speter while ((c = *p++) != '\0') 6038032Speter { 6138032Speter /* quote \ or " */ 6238032Speter if (c == '\\' || c == '"') 6338032Speter *q++ = '\\'; 6438032Speter *q++ = c; 6538032Speter } 6638032Speter *q++ = '"'; 6738032Speter *q = '\0'; 6838032Speter return r; 6938032Speter} 7090792Sgshapiro/* 7138032Speter** RFC822_STRING -- Checks string for proper RFC822 string quoting. 7238032Speter** 7338032Speter** Runs through a string and verifies RFC822 special characters 7438032Speter** are only found inside comments, quoted strings, or backslash 7538032Speter** escaped. Also verified balanced quotes and parenthesis. 7638032Speter** 7738032Speter** Parameters: 7838032Speter** s -- the string to modify. 7938032Speter** 8038032Speter** Returns: 8190792Sgshapiro** true iff the string is RFC822 compliant, false otherwise. 8238032Speter*/ 8338032Speter 8438032Speterbool 8538032Speterrfc822_string(s) 8638032Speter char *s; 8738032Speter{ 8890792Sgshapiro bool quoted = false; 8938032Speter int commentlev = 0; 9038032Speter char *c = s; 9138032Speter 9238032Speter if (s == NULL) 9390792Sgshapiro return false; 9438032Speter 9538032Speter while (*c != '\0') 9638032Speter { 9738032Speter /* escaped character */ 9838032Speter if (*c == '\\') 9938032Speter { 10038032Speter c++; 10138032Speter if (*c == '\0') 10290792Sgshapiro return false; 10338032Speter } 10438032Speter else if (commentlev == 0 && *c == '"') 10538032Speter quoted = !quoted; 10638032Speter else if (!quoted) 10738032Speter { 10838032Speter if (*c == ')') 10938032Speter { 11038032Speter /* unbalanced ')' */ 11138032Speter if (commentlev == 0) 11290792Sgshapiro return false; 11338032Speter else 11438032Speter commentlev--; 11538032Speter } 11638032Speter else if (*c == '(') 11738032Speter commentlev++; 11838032Speter else if (commentlev == 0 && 11938032Speter strchr(MustQuoteChars, *c) != NULL) 12090792Sgshapiro return false; 12138032Speter } 12238032Speter c++; 12338032Speter } 12490792Sgshapiro 12538032Speter /* unbalanced '"' or '(' */ 12690792Sgshapiro return !quoted && commentlev == 0; 12738032Speter} 12890792Sgshapiro/* 12942575Speter** SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string 13042575Speter** 13164562Sgshapiro** Arbitrarily shorten (in place) an RFC822 string and rebalance 13242575Speter** comments and quotes. 13342575Speter** 13442575Speter** Parameters: 13542575Speter** string -- the string to shorten 13642575Speter** length -- the maximum size, 0 if no maximum 13742575Speter** 13842575Speter** Returns: 13990792Sgshapiro** true if string is changed, false otherwise 14042575Speter** 14142575Speter** Side Effects: 14242575Speter** Changes string in place, possibly resulting 14342575Speter** in a shorter string. 14442575Speter*/ 14542575Speter 14642575Speterbool 14742575Spetershorten_rfc822_string(string, length) 14842575Speter char *string; 14942575Speter size_t length; 15042575Speter{ 15190792Sgshapiro bool backslash = false; 15290792Sgshapiro bool modified = false; 15390792Sgshapiro bool quoted = false; 15442575Speter size_t slen; 15542575Speter int parencount = 0; 15642575Speter char *ptr = string; 15764562Sgshapiro 15842575Speter /* 15942575Speter ** If have to rebalance an already short enough string, 16042575Speter ** need to do it within allocated space. 16142575Speter */ 16271345Sgshapiro 16342575Speter slen = strlen(string); 16442575Speter if (length == 0 || slen < length) 16542575Speter length = slen; 16642575Speter 16742575Speter while (*ptr != '\0') 16842575Speter { 16942575Speter if (backslash) 17042575Speter { 17190792Sgshapiro backslash = false; 17242575Speter goto increment; 17342575Speter } 17442575Speter 17542575Speter if (*ptr == '\\') 17690792Sgshapiro backslash = true; 17742575Speter else if (*ptr == '(') 17842575Speter { 17942575Speter if (!quoted) 18042575Speter parencount++; 18142575Speter } 18242575Speter else if (*ptr == ')') 18342575Speter { 18442575Speter if (--parencount < 0) 18542575Speter parencount = 0; 18642575Speter } 18764562Sgshapiro 18842575Speter /* Inside a comment, quotes don't matter */ 18942575Speter if (parencount <= 0 && *ptr == '"') 19042575Speter quoted = !quoted; 19142575Speter 19242575Speterincrement: 19342575Speter /* Check for sufficient space for next character */ 19464562Sgshapiro if (length - (ptr - string) <= (size_t) ((backslash ? 1 : 0) + 19542575Speter parencount + 19642575Speter (quoted ? 1 : 0))) 19742575Speter { 19842575Speter /* Not enough, backtrack */ 19942575Speter if (*ptr == '\\') 20090792Sgshapiro backslash = false; 20142575Speter else if (*ptr == '(' && !quoted) 20242575Speter parencount--; 20342575Speter else if (*ptr == '"' && parencount == 0) 20490792Sgshapiro quoted = false; 20542575Speter break; 20642575Speter } 20742575Speter ptr++; 20842575Speter } 20942575Speter 21042575Speter /* Rebalance */ 21142575Speter while (parencount-- > 0) 21242575Speter { 21342575Speter if (*ptr != ')') 21442575Speter { 21590792Sgshapiro modified = true; 21642575Speter *ptr = ')'; 21742575Speter } 21842575Speter ptr++; 21942575Speter } 22042575Speter if (quoted) 22142575Speter { 22242575Speter if (*ptr != '"') 22342575Speter { 22490792Sgshapiro modified = true; 22542575Speter *ptr = '"'; 22642575Speter } 22742575Speter ptr++; 22842575Speter } 22942575Speter if (*ptr != '\0') 23042575Speter { 23190792Sgshapiro modified = true; 23242575Speter *ptr = '\0'; 23342575Speter } 23442575Speter return modified; 23542575Speter} 23690792Sgshapiro/* 23742575Speter** FIND_CHARACTER -- find an unquoted character in an RFC822 string 23842575Speter** 23942575Speter** Find an unquoted, non-commented character in an RFC822 24042575Speter** string and return a pointer to its location in the 24142575Speter** string. 24242575Speter** 24342575Speter** Parameters: 24442575Speter** string -- the string to search 24542575Speter** character -- the character to find 24642575Speter** 24742575Speter** Returns: 24842575Speter** pointer to the character, or 24942575Speter** a pointer to the end of the line if character is not found 25042575Speter*/ 25142575Speter 25242575Speterchar * 25342575Speterfind_character(string, character) 25442575Speter char *string; 25564562Sgshapiro int character; 25642575Speter{ 25790792Sgshapiro bool backslash = false; 25890792Sgshapiro bool quoted = false; 25942575Speter int parencount = 0; 26064562Sgshapiro 26142575Speter while (string != NULL && *string != '\0') 26242575Speter { 26342575Speter if (backslash) 26442575Speter { 26590792Sgshapiro backslash = false; 26642575Speter if (!quoted && character == '\\' && *string == '\\') 26742575Speter break; 26842575Speter string++; 26942575Speter continue; 27042575Speter } 27142575Speter switch (*string) 27242575Speter { 27342575Speter case '\\': 27490792Sgshapiro backslash = true; 27542575Speter break; 27664562Sgshapiro 27742575Speter case '(': 27842575Speter if (!quoted) 27942575Speter parencount++; 28042575Speter break; 28164562Sgshapiro 28242575Speter case ')': 28342575Speter if (--parencount < 0) 28442575Speter parencount = 0; 28542575Speter break; 28642575Speter } 28764562Sgshapiro 28842575Speter /* Inside a comment, nothing matters */ 28942575Speter if (parencount > 0) 29042575Speter { 29142575Speter string++; 29242575Speter continue; 29342575Speter } 29464562Sgshapiro 29542575Speter if (*string == '"') 29642575Speter quoted = !quoted; 29742575Speter else if (*string == character && !quoted) 29842575Speter break; 29942575Speter string++; 30042575Speter } 30142575Speter 30242575Speter /* Return pointer to the character */ 30342575Speter return string; 30442575Speter} 30590792Sgshapiro 30690792Sgshapiro/* 30790792Sgshapiro** CHECK_BODYTYPE -- check bodytype parameter 30838032Speter** 30938032Speter** Parameters: 31090792Sgshapiro** bodytype -- bodytype parameter 31138032Speter** 31238032Speter** Returns: 31390792Sgshapiro** BODYTYPE_* according to parameter 31438032Speter** 31538032Speter*/ 31638032Speter 31790792Sgshapiroint 31890792Sgshapirocheck_bodytype(bodytype) 31990792Sgshapiro char *bodytype; 32038032Speter{ 32190792Sgshapiro /* check body type for legality */ 32290792Sgshapiro if (bodytype == NULL) 32390792Sgshapiro return BODYTYPE_NONE; 32490792Sgshapiro if (sm_strcasecmp(bodytype, "7BIT") == 0) 32590792Sgshapiro return BODYTYPE_7BIT; 32690792Sgshapiro if (sm_strcasecmp(bodytype, "8BITMIME") == 0) 32790792Sgshapiro return BODYTYPE_8BITMIME; 32890792Sgshapiro return BODYTYPE_ILLEGAL; 32990792Sgshapiro} 33038032Speter 33190792Sgshapiro#if _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI 33290792Sgshapiro/* 33390792Sgshapiro** TRUNCATE_AT_DELIM -- truncate string at a delimiter and append "..." 33477349Sgshapiro** 33577349Sgshapiro** Parameters: 33690792Sgshapiro** str -- string to truncate 33790792Sgshapiro** len -- maximum length (including '\0') (0 for unlimited) 33890792Sgshapiro** delim -- delimiter character 33977349Sgshapiro** 34077349Sgshapiro** Returns: 34190792Sgshapiro** None. 34277349Sgshapiro*/ 34377349Sgshapiro 34490792Sgshapirovoid 34590792Sgshapirotruncate_at_delim(str, len, delim) 34690792Sgshapiro char *str; 34790792Sgshapiro size_t len; 34890792Sgshapiro int delim; 34977349Sgshapiro{ 35090792Sgshapiro char *p; 35177349Sgshapiro 35290792Sgshapiro if (str == NULL || len == 0 || strlen(str) < len) 35390792Sgshapiro return; 35477349Sgshapiro 35590792Sgshapiro *(str + len - 1) = '\0'; 35690792Sgshapiro while ((p = strrchr(str, delim)) != NULL) 35777349Sgshapiro { 35890792Sgshapiro *p = '\0'; 35990792Sgshapiro if (p - str + 4 < len) 36090792Sgshapiro { 36190792Sgshapiro *p++ = ':'; 36290792Sgshapiro *p = '\0'; 36390792Sgshapiro (void) sm_strlcat(str, "...", len); 36490792Sgshapiro return; 36590792Sgshapiro } 36690792Sgshapiro } 36777349Sgshapiro 36890792Sgshapiro /* Couldn't find a place to append "..." */ 36990792Sgshapiro if (len > 3) 37090792Sgshapiro (void) sm_strlcpy(str, "...", len); 37190792Sgshapiro else 37290792Sgshapiro str[0] = '\0'; 37377349Sgshapiro} 37490792Sgshapiro#endif /* _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI */ 37590792Sgshapiro/* 37690792Sgshapiro** XALLOC -- Allocate memory, raise an exception on error 37777349Sgshapiro** 37877349Sgshapiro** Parameters: 37990792Sgshapiro** sz -- size of area to allocate. 38077349Sgshapiro** 38177349Sgshapiro** Returns: 38277349Sgshapiro** pointer to data region. 38377349Sgshapiro** 38490792Sgshapiro** Exceptions: 38590792Sgshapiro** SmHeapOutOfMemory (F:sm.heap) -- cannot allocate memory 38690792Sgshapiro** 38777349Sgshapiro** Side Effects: 38877349Sgshapiro** Memory is allocated. 38977349Sgshapiro*/ 39077349Sgshapiro 39177349Sgshapirochar * 39290792Sgshapiro#if SM_HEAP_CHECK 39390792Sgshapiroxalloc_tagged(sz, file, line) 39490792Sgshapiro register int sz; 39590792Sgshapiro char *file; 39690792Sgshapiro int line; 39790792Sgshapiro#else /* SM_HEAP_CHECK */ 39890792Sgshapiroxalloc(sz) 39990792Sgshapiro register int sz; 40090792Sgshapiro#endif /* SM_HEAP_CHECK */ 40177349Sgshapiro{ 40277349Sgshapiro register char *p; 40377349Sgshapiro 40477349Sgshapiro /* some systems can't handle size zero mallocs */ 40577349Sgshapiro if (sz <= 0) 40677349Sgshapiro sz = 1; 40777349Sgshapiro 40890792Sgshapiro /* scaffolding for testing error handling code */ 40990792Sgshapiro sm_xtrap_raise_x(&SmHeapOutOfMemory); 41090792Sgshapiro 41190792Sgshapiro p = sm_malloc_tagged((unsigned) sz, file, line, sm_heap_group()); 41277349Sgshapiro if (p == NULL) 41377349Sgshapiro { 41490792Sgshapiro sm_exc_raise_x(&SmHeapOutOfMemory); 41577349Sgshapiro } 41677349Sgshapiro return p; 41777349Sgshapiro} 41890792Sgshapiro/* 41938032Speter** COPYPLIST -- copy list of pointers. 42038032Speter** 42190792Sgshapiro** This routine is the equivalent of strdup for lists of 42238032Speter** pointers. 42338032Speter** 42438032Speter** Parameters: 42538032Speter** list -- list of pointers to copy. 42638032Speter** Must be NULL terminated. 42790792Sgshapiro** copycont -- if true, copy the contents of the vector 42838032Speter** (which must be a string) also. 42990792Sgshapiro** rpool -- resource pool from which to allocate storage, 43090792Sgshapiro** or NULL 43138032Speter** 43238032Speter** Returns: 43338032Speter** a copy of 'list'. 43438032Speter*/ 43538032Speter 43638032Speterchar ** 43790792Sgshapirocopyplist(list, copycont, rpool) 43838032Speter char **list; 43938032Speter bool copycont; 44090792Sgshapiro SM_RPOOL_T *rpool; 44138032Speter{ 44238032Speter register char **vp; 44338032Speter register char **newvp; 44438032Speter 44538032Speter for (vp = list; *vp != NULL; vp++) 44638032Speter continue; 44738032Speter 44838032Speter vp++; 44938032Speter 45090792Sgshapiro newvp = (char **) sm_rpool_malloc_x(rpool, (vp - list) * sizeof *vp); 45164562Sgshapiro memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof *vp); 45238032Speter 45338032Speter if (copycont) 45438032Speter { 45538032Speter for (vp = newvp; *vp != NULL; vp++) 45690792Sgshapiro *vp = sm_rpool_strdup_x(rpool, *vp); 45738032Speter } 45838032Speter 45964562Sgshapiro return newvp; 46038032Speter} 46190792Sgshapiro/* 46238032Speter** COPYQUEUE -- copy address queue. 46338032Speter** 46490792Sgshapiro** This routine is the equivalent of strdup for address queues; 46564562Sgshapiro** addresses marked as QS_IS_DEAD() aren't copied 46638032Speter** 46738032Speter** Parameters: 46838032Speter** addr -- list of address structures to copy. 46990792Sgshapiro** rpool -- resource pool from which to allocate storage 47038032Speter** 47138032Speter** Returns: 47238032Speter** a copy of 'addr'. 47338032Speter*/ 47438032Speter 47538032SpeterADDRESS * 47690792Sgshapirocopyqueue(addr, rpool) 47738032Speter ADDRESS *addr; 47890792Sgshapiro SM_RPOOL_T *rpool; 47938032Speter{ 48038032Speter register ADDRESS *newaddr; 48138032Speter ADDRESS *ret; 48238032Speter register ADDRESS **tail = &ret; 48338032Speter 48438032Speter while (addr != NULL) 48538032Speter { 48664562Sgshapiro if (!QS_IS_DEAD(addr->q_state)) 48738032Speter { 48890792Sgshapiro newaddr = (ADDRESS *) sm_rpool_malloc_x(rpool, 48990792Sgshapiro sizeof *newaddr); 49038032Speter STRUCTCOPY(*addr, *newaddr); 49138032Speter *tail = newaddr; 49238032Speter tail = &newaddr->q_next; 49338032Speter } 49438032Speter addr = addr->q_next; 49538032Speter } 49638032Speter *tail = NULL; 49764562Sgshapiro 49838032Speter return ret; 49938032Speter} 50090792Sgshapiro/* 50164562Sgshapiro** LOG_SENDMAIL_PID -- record sendmail pid and command line. 50264562Sgshapiro** 50364562Sgshapiro** Parameters: 50464562Sgshapiro** e -- the current envelope. 50564562Sgshapiro** 50664562Sgshapiro** Returns: 50764562Sgshapiro** none. 50864562Sgshapiro** 50964562Sgshapiro** Side Effects: 51090792Sgshapiro** writes pidfile, logs command line. 51164562Sgshapiro*/ 51264562Sgshapiro 51364562Sgshapirovoid 51464562Sgshapirolog_sendmail_pid(e) 51564562Sgshapiro ENVELOPE *e; 51664562Sgshapiro{ 51764562Sgshapiro long sff; 51890792Sgshapiro SM_FILE_T *pidf; 51998121Sgshapiro char pidpath[MAXPATHLEN]; 52090792Sgshapiro extern char *CommandLineArgs; 52164562Sgshapiro 52264562Sgshapiro /* write the pid to the log file for posterity */ 52364562Sgshapiro sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT; 52464562Sgshapiro if (TrustedUid != 0 && RealUid == TrustedUid) 52564562Sgshapiro sff |= SFF_OPENASROOT; 52664562Sgshapiro expand(PidFile, pidpath, sizeof pidpath, e); 52798121Sgshapiro pidf = safefopen(pidpath, O_WRONLY|O_TRUNC, FileMode, sff); 52864562Sgshapiro if (pidf == NULL) 52964562Sgshapiro { 53073188Sgshapiro sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s", 53190792Sgshapiro pidpath, sm_errstring(errno)); 53264562Sgshapiro } 53364562Sgshapiro else 53464562Sgshapiro { 53577349Sgshapiro pid_t pid; 53664562Sgshapiro 53777349Sgshapiro pid = getpid(); 53871345Sgshapiro 53964562Sgshapiro /* write the process id on line 1 */ 54090792Sgshapiro (void) sm_io_fprintf(pidf, SM_TIME_DEFAULT, "%ld\n", 54190792Sgshapiro (long) pid); 54264562Sgshapiro 54364562Sgshapiro /* line 2 contains all command line flags */ 54490792Sgshapiro (void) sm_io_fprintf(pidf, SM_TIME_DEFAULT, "%s\n", 54590792Sgshapiro CommandLineArgs); 54664562Sgshapiro 54764562Sgshapiro /* flush and close */ 54890792Sgshapiro (void) sm_io_close(pidf, SM_TIME_DEFAULT); 54964562Sgshapiro } 55090792Sgshapiro if (LogLevel > 9) 55190792Sgshapiro sm_syslog(LOG_INFO, NOQID, "started as: %s", CommandLineArgs); 55264562Sgshapiro} 55390792Sgshapiro/* 55464562Sgshapiro** SET_DELIVERY_MODE -- set and record the delivery mode 55564562Sgshapiro** 55664562Sgshapiro** Parameters: 55764562Sgshapiro** mode -- delivery mode 55864562Sgshapiro** e -- the current envelope. 55964562Sgshapiro** 56064562Sgshapiro** Returns: 56164562Sgshapiro** none. 56264562Sgshapiro** 56364562Sgshapiro** Side Effects: 56490792Sgshapiro** sets {deliveryMode} macro 56564562Sgshapiro*/ 56664562Sgshapiro 56764562Sgshapirovoid 56864562Sgshapiroset_delivery_mode(mode, e) 56964562Sgshapiro int mode; 57064562Sgshapiro ENVELOPE *e; 57164562Sgshapiro{ 57264562Sgshapiro char buf[2]; 57364562Sgshapiro 57490792Sgshapiro e->e_sendmode = (char) mode; 57590792Sgshapiro buf[0] = (char) mode; 57664562Sgshapiro buf[1] = '\0'; 57790792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{deliveryMode}"), buf); 57864562Sgshapiro} 57990792Sgshapiro/* 58090792Sgshapiro** SET_OP_MODE -- set and record the op mode 58190792Sgshapiro** 58290792Sgshapiro** Parameters: 58390792Sgshapiro** mode -- op mode 58490792Sgshapiro** e -- the current envelope. 58590792Sgshapiro** 58690792Sgshapiro** Returns: 58790792Sgshapiro** none. 58890792Sgshapiro** 58990792Sgshapiro** Side Effects: 59090792Sgshapiro** sets {opMode} macro 59190792Sgshapiro*/ 59290792Sgshapiro 59390792Sgshapirovoid 59490792Sgshapiroset_op_mode(mode) 59590792Sgshapiro int mode; 59690792Sgshapiro{ 59790792Sgshapiro char buf[2]; 59890792Sgshapiro extern ENVELOPE BlankEnvelope; 59990792Sgshapiro 60090792Sgshapiro OpMode = (char) mode; 60190792Sgshapiro buf[0] = (char) mode; 60290792Sgshapiro buf[1] = '\0'; 60390792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, MID_OPMODE, buf); 60490792Sgshapiro} 60590792Sgshapiro/* 60638032Speter** PRINTAV -- print argument vector. 60738032Speter** 60838032Speter** Parameters: 60938032Speter** av -- argument vector. 61038032Speter** 61138032Speter** Returns: 61238032Speter** none. 61338032Speter** 61438032Speter** Side Effects: 61538032Speter** prints av. 61638032Speter*/ 61738032Speter 61838032Spetervoid 61938032Speterprintav(av) 62038032Speter register char **av; 62138032Speter{ 62238032Speter while (*av != NULL) 62338032Speter { 62438032Speter if (tTd(0, 44)) 62590792Sgshapiro sm_dprintf("\n\t%08lx=", (unsigned long) *av); 62638032Speter else 62790792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, ' '); 62838032Speter xputs(*av++); 62938032Speter } 63090792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, '\n'); 63138032Speter} 63290792Sgshapiro/* 63338032Speter** XPUTS -- put string doing control escapes. 63438032Speter** 63538032Speter** Parameters: 63638032Speter** s -- string to put. 63738032Speter** 63838032Speter** Returns: 63938032Speter** none. 64038032Speter** 64138032Speter** Side Effects: 64238032Speter** output to stdout 64338032Speter*/ 64438032Speter 64538032Spetervoid 64638032Speterxputs(s) 64738032Speter register const char *s; 64838032Speter{ 64938032Speter register int c; 65038032Speter register struct metamac *mp; 65190792Sgshapiro bool shiftout = false; 65238032Speter extern struct metamac MetaMacros[]; 65390792Sgshapiro static SM_DEBUG_T DebugANSI = SM_DEBUG_INITIALIZER("ANSI", 65490792Sgshapiro "@(#)$Debug: ANSI - enable reverse video in debug output $"); 65538032Speter 65690792Sgshapiro /* 65790792Sgshapiro ** TermEscape is set here, rather than in main(), 65890792Sgshapiro ** because ANSI mode can be turned on or off at any time 65990792Sgshapiro ** if we are in -bt rule testing mode. 66090792Sgshapiro */ 66190792Sgshapiro 66290792Sgshapiro if (sm_debug_unknown(&DebugANSI)) 66390792Sgshapiro { 66490792Sgshapiro if (sm_debug_active(&DebugANSI, 1)) 66590792Sgshapiro { 66690792Sgshapiro TermEscape.te_rv_on = "\033[7m"; 66790792Sgshapiro TermEscape.te_rv_off = "\033[0m"; 66890792Sgshapiro } 66990792Sgshapiro else 67090792Sgshapiro { 67190792Sgshapiro TermEscape.te_rv_on = ""; 67290792Sgshapiro TermEscape.te_rv_off = ""; 67390792Sgshapiro } 67490792Sgshapiro } 67590792Sgshapiro 67638032Speter if (s == NULL) 67738032Speter { 67890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s<null>%s", 67990792Sgshapiro TermEscape.te_rv_on, TermEscape.te_rv_off); 68038032Speter return; 68138032Speter } 68238032Speter while ((c = (*s++ & 0377)) != '\0') 68338032Speter { 68438032Speter if (shiftout) 68538032Speter { 68690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s", 68790792Sgshapiro TermEscape.te_rv_off); 68890792Sgshapiro shiftout = false; 68938032Speter } 69038032Speter if (!isascii(c)) 69138032Speter { 69238032Speter if (c == MATCHREPL) 69338032Speter { 69490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 69590792Sgshapiro "%s$", 69690792Sgshapiro TermEscape.te_rv_on); 69790792Sgshapiro shiftout = true; 69838032Speter if (*s == '\0') 69938032Speter continue; 70038032Speter c = *s++ & 0377; 70138032Speter goto printchar; 70238032Speter } 70338032Speter if (c == MACROEXPAND || c == MACRODEXPAND) 70438032Speter { 70590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 70690792Sgshapiro "%s$", 70790792Sgshapiro TermEscape.te_rv_on); 70838032Speter if (c == MACRODEXPAND) 70990792Sgshapiro (void) sm_io_putc(smioout, 71090792Sgshapiro SM_TIME_DEFAULT, '&'); 71190792Sgshapiro shiftout = true; 71238032Speter if (*s == '\0') 71338032Speter continue; 71438032Speter if (strchr("=~&?", *s) != NULL) 71590792Sgshapiro (void) sm_io_putc(smioout, 71690792Sgshapiro SM_TIME_DEFAULT, 71790792Sgshapiro *s++); 71838032Speter if (bitset(0200, *s)) 71990792Sgshapiro (void) sm_io_fprintf(smioout, 72090792Sgshapiro SM_TIME_DEFAULT, 72190792Sgshapiro "{%s}", 72290792Sgshapiro macname(bitidx(*s++))); 72338032Speter else 72490792Sgshapiro (void) sm_io_fprintf(smioout, 72590792Sgshapiro SM_TIME_DEFAULT, 72690792Sgshapiro "%c", 72790792Sgshapiro *s++); 72838032Speter continue; 72938032Speter } 73038032Speter for (mp = MetaMacros; mp->metaname != '\0'; mp++) 73138032Speter { 73290792Sgshapiro if (bitidx(mp->metaval) == c) 73338032Speter { 73490792Sgshapiro (void) sm_io_fprintf(smioout, 73590792Sgshapiro SM_TIME_DEFAULT, 73690792Sgshapiro "%s$%c", 73790792Sgshapiro TermEscape.te_rv_on, 73890792Sgshapiro mp->metaname); 73990792Sgshapiro shiftout = true; 74038032Speter break; 74138032Speter } 74238032Speter } 74338032Speter if (c == MATCHCLASS || c == MATCHNCLASS) 74438032Speter { 74538032Speter if (bitset(0200, *s)) 74690792Sgshapiro (void) sm_io_fprintf(smioout, 74790792Sgshapiro SM_TIME_DEFAULT, 74890792Sgshapiro "{%s}", 74990792Sgshapiro macname(bitidx(*s++))); 75038032Speter else if (*s != '\0') 75190792Sgshapiro (void) sm_io_fprintf(smioout, 75290792Sgshapiro SM_TIME_DEFAULT, 75390792Sgshapiro "%c", 75490792Sgshapiro *s++); 75538032Speter } 75638032Speter if (mp->metaname != '\0') 75738032Speter continue; 75838032Speter 75938032Speter /* unrecognized meta character */ 76090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%sM-", 76190792Sgshapiro TermEscape.te_rv_on); 76290792Sgshapiro shiftout = true; 76338032Speter c &= 0177; 76438032Speter } 76538032Speter printchar: 76638032Speter if (isprint(c)) 76738032Speter { 76890792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); 76938032Speter continue; 77038032Speter } 77138032Speter 77238032Speter /* wasn't a meta-macro -- find another way to print it */ 77338032Speter switch (c) 77438032Speter { 77538032Speter case '\n': 77638032Speter c = 'n'; 77738032Speter break; 77838032Speter 77938032Speter case '\r': 78038032Speter c = 'r'; 78138032Speter break; 78238032Speter 78338032Speter case '\t': 78438032Speter c = 't'; 78538032Speter break; 78638032Speter } 78738032Speter if (!shiftout) 78838032Speter { 78990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s", 79090792Sgshapiro TermEscape.te_rv_on); 79190792Sgshapiro shiftout = true; 79238032Speter } 79338032Speter if (isprint(c)) 79438032Speter { 79590792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, '\\'); 79690792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); 79738032Speter } 79838032Speter else 79938032Speter { 80090792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, '^'); 80190792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c ^ 0100); 80238032Speter } 80338032Speter } 80438032Speter if (shiftout) 80590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s", 80690792Sgshapiro TermEscape.te_rv_off); 80790792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 80838032Speter} 80990792Sgshapiro/* 81038032Speter** MAKELOWER -- Translate a line into lower case 81138032Speter** 81238032Speter** Parameters: 81338032Speter** p -- the string to translate. If NULL, return is 81438032Speter** immediate. 81538032Speter** 81638032Speter** Returns: 81738032Speter** none. 81838032Speter** 81938032Speter** Side Effects: 82038032Speter** String pointed to by p is translated to lower case. 82138032Speter*/ 82238032Speter 82338032Spetervoid 82438032Spetermakelower(p) 82538032Speter register char *p; 82638032Speter{ 82738032Speter register char c; 82838032Speter 82938032Speter if (p == NULL) 83038032Speter return; 83138032Speter for (; (c = *p) != '\0'; p++) 83238032Speter if (isascii(c) && isupper(c)) 83338032Speter *p = tolower(c); 83438032Speter} 83590792Sgshapiro/* 83638032Speter** FIXCRLF -- fix <CR><LF> in line. 83738032Speter** 83838032Speter** Looks for the <CR><LF> combination and turns it into the 83938032Speter** UNIX canonical <NL> character. It only takes one line, 84038032Speter** i.e., it is assumed that the first <NL> found is the end 84138032Speter** of the line. 84238032Speter** 84338032Speter** Parameters: 84438032Speter** line -- the line to fix. 84538032Speter** stripnl -- if true, strip the newline also. 84638032Speter** 84738032Speter** Returns: 84838032Speter** none. 84938032Speter** 85038032Speter** Side Effects: 85138032Speter** line is changed in place. 85238032Speter*/ 85338032Speter 85438032Spetervoid 85538032Speterfixcrlf(line, stripnl) 85638032Speter char *line; 85738032Speter bool stripnl; 85838032Speter{ 85938032Speter register char *p; 86038032Speter 86138032Speter p = strchr(line, '\n'); 86238032Speter if (p == NULL) 86338032Speter return; 86438032Speter if (p > line && p[-1] == '\r') 86538032Speter p--; 86638032Speter if (!stripnl) 86738032Speter *p++ = '\n'; 86838032Speter *p = '\0'; 86938032Speter} 87090792Sgshapiro/* 87138032Speter** PUTLINE -- put a line like fputs obeying SMTP conventions 87238032Speter** 87338032Speter** This routine always guarantees outputing a newline (or CRLF, 87438032Speter** as appropriate) at the end of the string. 87538032Speter** 87638032Speter** Parameters: 87738032Speter** l -- line to put. 87838032Speter** mci -- the mailer connection information. 87938032Speter** 88038032Speter** Returns: 88138032Speter** none 88238032Speter** 88338032Speter** Side Effects: 88490792Sgshapiro** output of l to mci->mci_out. 88538032Speter*/ 88638032Speter 88738032Spetervoid 88838032Speterputline(l, mci) 88938032Speter register char *l; 89038032Speter register MCI *mci; 89138032Speter{ 89238032Speter putxline(l, strlen(l), mci, PXLF_MAPFROM); 89338032Speter} 89490792Sgshapiro/* 89538032Speter** PUTXLINE -- putline with flags bits. 89638032Speter** 89738032Speter** This routine always guarantees outputing a newline (or CRLF, 89838032Speter** as appropriate) at the end of the string. 89938032Speter** 90038032Speter** Parameters: 90138032Speter** l -- line to put. 90238032Speter** len -- the length of the line. 90338032Speter** mci -- the mailer connection information. 90438032Speter** pxflags -- flag bits: 90538032Speter** PXLF_MAPFROM -- map From_ to >From_. 90638032Speter** PXLF_STRIP8BIT -- strip 8th bit. 90738032Speter** PXLF_HEADER -- map bare newline in header to newline space. 90894334Sgshapiro** PXLF_NOADDEOL -- don't add an EOL if one wasn't present. 90938032Speter** 91038032Speter** Returns: 91138032Speter** none 91238032Speter** 91338032Speter** Side Effects: 91490792Sgshapiro** output of l to mci->mci_out. 91538032Speter*/ 91638032Speter 91738032Spetervoid 91838032Speterputxline(l, len, mci, pxflags) 91938032Speter register char *l; 92038032Speter size_t len; 92138032Speter register MCI *mci; 92238032Speter int pxflags; 92338032Speter{ 92490792Sgshapiro bool dead = false; 92538032Speter register char *p, *end; 92638032Speter int slop = 0; 92738032Speter 92838032Speter /* strip out 0200 bits -- these can look like TELNET protocol */ 92938032Speter if (bitset(MCIF_7BIT, mci->mci_flags) || 93038032Speter bitset(PXLF_STRIP8BIT, pxflags)) 93138032Speter { 93238032Speter register char svchar; 93338032Speter 93438032Speter for (p = l; (svchar = *p) != '\0'; ++p) 93538032Speter if (bitset(0200, svchar)) 93638032Speter *p = svchar &~ 0200; 93738032Speter } 93838032Speter 93938032Speter end = l + len; 94038032Speter do 94138032Speter { 94294334Sgshapiro bool noeol = false; 94394334Sgshapiro 94438032Speter /* find the end of the line */ 94538032Speter p = memchr(l, '\n', end - l); 94638032Speter if (p == NULL) 94794334Sgshapiro { 94838032Speter p = end; 94994334Sgshapiro noeol = true; 95094334Sgshapiro } 95138032Speter 95238032Speter if (TrafficLogFile != NULL) 95390792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 95490792Sgshapiro "%05d >>> ", (int) CurrentPid); 95538032Speter 95638032Speter /* check for line overflow */ 95738032Speter while (mci->mci_mailer->m_linelimit > 0 && 95838032Speter (p - l + slop) > mci->mci_mailer->m_linelimit) 95938032Speter { 96038032Speter char *l_base = l; 96138032Speter register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1]; 96238032Speter 96338032Speter if (l[0] == '.' && slop == 0 && 96438032Speter bitnset(M_XDOT, mci->mci_mailer->m_flags)) 96538032Speter { 96690792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 96790792Sgshapiro '.') == SM_IO_EOF) 96890792Sgshapiro dead = true; 96971345Sgshapiro else 97071345Sgshapiro { 97171345Sgshapiro /* record progress for DATA timeout */ 97290792Sgshapiro DataProgress = true; 97371345Sgshapiro } 97438032Speter if (TrafficLogFile != NULL) 97590792Sgshapiro (void) sm_io_putc(TrafficLogFile, 97690792Sgshapiro SM_TIME_DEFAULT, '.'); 97738032Speter } 97838032Speter else if (l[0] == 'F' && slop == 0 && 97938032Speter bitset(PXLF_MAPFROM, pxflags) && 98038032Speter strncmp(l, "From ", 5) == 0 && 98138032Speter bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 98238032Speter { 98390792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 98490792Sgshapiro '>') == SM_IO_EOF) 98590792Sgshapiro dead = true; 98671345Sgshapiro else 98771345Sgshapiro { 98871345Sgshapiro /* record progress for DATA timeout */ 98990792Sgshapiro DataProgress = true; 99071345Sgshapiro } 99138032Speter if (TrafficLogFile != NULL) 99290792Sgshapiro (void) sm_io_putc(TrafficLogFile, 99390792Sgshapiro SM_TIME_DEFAULT, 99490792Sgshapiro '>'); 99538032Speter } 99664562Sgshapiro if (dead) 99764562Sgshapiro break; 99864562Sgshapiro 99938032Speter while (l < q) 100038032Speter { 100190792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 100290792Sgshapiro (unsigned char) *l++) == SM_IO_EOF) 100364562Sgshapiro { 100490792Sgshapiro dead = true; 100564562Sgshapiro break; 100664562Sgshapiro } 100771345Sgshapiro else 100871345Sgshapiro { 100971345Sgshapiro /* record progress for DATA timeout */ 101090792Sgshapiro DataProgress = true; 101171345Sgshapiro } 101238032Speter } 101364562Sgshapiro if (dead) 101464562Sgshapiro break; 101564562Sgshapiro 101690792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '!') == 101790792Sgshapiro SM_IO_EOF || 101890792Sgshapiro sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 101990792Sgshapiro mci->mci_mailer->m_eol) == 102090792Sgshapiro SM_IO_EOF || 102190792Sgshapiro sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, ' ') == 102290792Sgshapiro SM_IO_EOF) 102364562Sgshapiro { 102490792Sgshapiro dead = true; 102564562Sgshapiro break; 102664562Sgshapiro } 102771345Sgshapiro else 102871345Sgshapiro { 102971345Sgshapiro /* record progress for DATA timeout */ 103090792Sgshapiro DataProgress = true; 103171345Sgshapiro } 103238032Speter if (TrafficLogFile != NULL) 103338032Speter { 103438032Speter for (l = l_base; l < q; l++) 103590792Sgshapiro (void) sm_io_putc(TrafficLogFile, 103690792Sgshapiro SM_TIME_DEFAULT, 103790792Sgshapiro (unsigned char)*l); 103890792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 103990792Sgshapiro SM_TIME_DEFAULT, 104090792Sgshapiro "!\n%05d >>> ", 104190792Sgshapiro (int) CurrentPid); 104238032Speter } 104338032Speter slop = 1; 104438032Speter } 104538032Speter 104664562Sgshapiro if (dead) 104764562Sgshapiro break; 104864562Sgshapiro 104938032Speter /* output last part */ 105038032Speter if (l[0] == '.' && slop == 0 && 105138032Speter bitnset(M_XDOT, mci->mci_mailer->m_flags)) 105238032Speter { 105390792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '.') == 105490792Sgshapiro SM_IO_EOF) 105564562Sgshapiro break; 105671345Sgshapiro else 105771345Sgshapiro { 105871345Sgshapiro /* record progress for DATA timeout */ 105990792Sgshapiro DataProgress = true; 106071345Sgshapiro } 106138032Speter if (TrafficLogFile != NULL) 106290792Sgshapiro (void) sm_io_putc(TrafficLogFile, 106390792Sgshapiro SM_TIME_DEFAULT, '.'); 106438032Speter } 106538032Speter else if (l[0] == 'F' && slop == 0 && 106638032Speter bitset(PXLF_MAPFROM, pxflags) && 106738032Speter strncmp(l, "From ", 5) == 0 && 106838032Speter bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 106938032Speter { 107090792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '>') == 107190792Sgshapiro SM_IO_EOF) 107264562Sgshapiro break; 107371345Sgshapiro else 107471345Sgshapiro { 107571345Sgshapiro /* record progress for DATA timeout */ 107690792Sgshapiro DataProgress = true; 107771345Sgshapiro } 107838032Speter if (TrafficLogFile != NULL) 107990792Sgshapiro (void) sm_io_putc(TrafficLogFile, 108090792Sgshapiro SM_TIME_DEFAULT, '>'); 108138032Speter } 108238032Speter for ( ; l < p; ++l) 108338032Speter { 108438032Speter if (TrafficLogFile != NULL) 108590792Sgshapiro (void) sm_io_putc(TrafficLogFile, 108690792Sgshapiro SM_TIME_DEFAULT, 108790792Sgshapiro (unsigned char)*l); 108890792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 108990792Sgshapiro (unsigned char) *l) == SM_IO_EOF) 109064562Sgshapiro { 109190792Sgshapiro dead = true; 109264562Sgshapiro break; 109364562Sgshapiro } 109471345Sgshapiro else 109571345Sgshapiro { 109671345Sgshapiro /* record progress for DATA timeout */ 109790792Sgshapiro DataProgress = true; 109871345Sgshapiro } 109938032Speter } 110064562Sgshapiro if (dead) 110164562Sgshapiro break; 110264562Sgshapiro 110338032Speter if (TrafficLogFile != NULL) 110490792Sgshapiro (void) sm_io_putc(TrafficLogFile, SM_TIME_DEFAULT, 110590792Sgshapiro '\n'); 110694334Sgshapiro if ((!bitset(PXLF_NOADDEOL, pxflags) || !noeol) && 110794334Sgshapiro sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 110890792Sgshapiro mci->mci_mailer->m_eol) == SM_IO_EOF) 110964562Sgshapiro break; 111071345Sgshapiro else 111171345Sgshapiro { 111271345Sgshapiro /* record progress for DATA timeout */ 111390792Sgshapiro DataProgress = true; 111471345Sgshapiro } 111538032Speter if (l < end && *l == '\n') 111638032Speter { 111738032Speter if (*++l != ' ' && *l != '\t' && *l != '\0' && 111838032Speter bitset(PXLF_HEADER, pxflags)) 111938032Speter { 112090792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 112190792Sgshapiro ' ') == SM_IO_EOF) 112264562Sgshapiro break; 112371345Sgshapiro else 112471345Sgshapiro { 112571345Sgshapiro /* record progress for DATA timeout */ 112690792Sgshapiro DataProgress = true; 112771345Sgshapiro } 112890792Sgshapiro 112938032Speter if (TrafficLogFile != NULL) 113090792Sgshapiro (void) sm_io_putc(TrafficLogFile, 113190792Sgshapiro SM_TIME_DEFAULT, ' '); 113238032Speter } 113338032Speter } 113490792Sgshapiro 113590792Sgshapiro /* record progress for DATA timeout */ 113690792Sgshapiro DataProgress = true; 113738032Speter } while (l < end); 113838032Speter} 113990792Sgshapiro/* 114038032Speter** XUNLINK -- unlink a file, doing logging as appropriate. 114138032Speter** 114238032Speter** Parameters: 114338032Speter** f -- name of file to unlink. 114438032Speter** 114538032Speter** Returns: 114690792Sgshapiro** return value of unlink() 114738032Speter** 114838032Speter** Side Effects: 114938032Speter** f is unlinked. 115038032Speter*/ 115138032Speter 115290792Sgshapiroint 115338032Speterxunlink(f) 115438032Speter char *f; 115538032Speter{ 115638032Speter register int i; 115790792Sgshapiro int save_errno; 115838032Speter 115938032Speter if (LogLevel > 98) 116090792Sgshapiro sm_syslog(LOG_DEBUG, CurEnv->e_id, "unlink %s", f); 116138032Speter 116238032Speter i = unlink(f); 116390792Sgshapiro save_errno = errno; 116438032Speter if (i < 0 && LogLevel > 97) 116590792Sgshapiro sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s: unlink-fail %d", 116664562Sgshapiro f, errno); 116790792Sgshapiro if (i >= 0) 116890792Sgshapiro SYNC_DIR(f, false); 116990792Sgshapiro errno = save_errno; 117090792Sgshapiro return i; 117138032Speter} 117290792Sgshapiro/* 117338032Speter** SFGETS -- "safe" fgets -- times out and ignores random interrupts. 117438032Speter** 117538032Speter** Parameters: 117638032Speter** buf -- place to put the input line. 117738032Speter** siz -- size of buf. 117838032Speter** fp -- file to read from. 117938032Speter** timeout -- the timeout before error occurs. 118038032Speter** during -- what we are trying to read (for error messages). 118138032Speter** 118238032Speter** Returns: 118390792Sgshapiro** NULL on error (including timeout). This may also leave 118438032Speter** buf containing a null string. 118538032Speter** buf otherwise. 118638032Speter*/ 118738032Speter 118864562Sgshapiro 118938032Speterchar * 119038032Spetersfgets(buf, siz, fp, timeout, during) 119138032Speter char *buf; 119238032Speter int siz; 119390792Sgshapiro SM_FILE_T *fp; 119438032Speter time_t timeout; 119538032Speter char *during; 119638032Speter{ 119738032Speter register char *p; 119843730Speter int save_errno; 119990792Sgshapiro int io_timeout; 120038032Speter 120190792Sgshapiro SM_REQUIRE(siz > 0); 120290792Sgshapiro SM_REQUIRE(buf != NULL); 120390792Sgshapiro 120438032Speter if (fp == NULL) 120538032Speter { 120638032Speter buf[0] = '\0'; 120790792Sgshapiro errno = EBADF; 120838032Speter return NULL; 120938032Speter } 121038032Speter 121190792Sgshapiro /* try to read */ 121290792Sgshapiro p = NULL; 121390792Sgshapiro errno = 0; 121490792Sgshapiro 121590792Sgshapiro /* convert the timeout to sm_io notation */ 121690792Sgshapiro io_timeout = (timeout <= 0) ? SM_TIME_DEFAULT : timeout * 1000; 121790792Sgshapiro while (!sm_io_eof(fp) && !sm_io_error(fp)) 121838032Speter { 121990792Sgshapiro errno = 0; 122090792Sgshapiro p = sm_io_fgets(fp, io_timeout, buf, siz); 122190792Sgshapiro if (p == NULL && errno == EAGAIN) 122238032Speter { 122390792Sgshapiro /* The sm_io_fgets() call timedout */ 122438032Speter if (LogLevel > 1) 122538032Speter sm_syslog(LOG_NOTICE, CurEnv->e_id, 122664562Sgshapiro "timeout waiting for input from %.100s during %s", 122790792Sgshapiro CURHOSTNAME, 122864562Sgshapiro during); 122938032Speter buf[0] = '\0'; 123038032Speter#if XDEBUG 123138032Speter checkfd012(during); 123264562Sgshapiro#endif /* XDEBUG */ 123338032Speter if (TrafficLogFile != NULL) 123490792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 123590792Sgshapiro SM_TIME_DEFAULT, 123690792Sgshapiro "%05d <<< [TIMEOUT]\n", 123790792Sgshapiro (int) CurrentPid); 123890792Sgshapiro errno = ETIMEDOUT; 123964562Sgshapiro return NULL; 124038032Speter } 124138032Speter if (p != NULL || errno != EINTR) 124238032Speter break; 124390792Sgshapiro (void) sm_io_clearerr(fp); 124438032Speter } 124543730Speter save_errno = errno; 124638032Speter 124738032Speter /* clean up the books and exit */ 124838032Speter LineNumber++; 124938032Speter if (p == NULL) 125038032Speter { 125138032Speter buf[0] = '\0'; 125238032Speter if (TrafficLogFile != NULL) 125390792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 125490792Sgshapiro "%05d <<< [EOF]\n", 125590792Sgshapiro (int) CurrentPid); 125643730Speter errno = save_errno; 125764562Sgshapiro return NULL; 125838032Speter } 125938032Speter if (TrafficLogFile != NULL) 126090792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 126190792Sgshapiro "%05d <<< %s", (int) CurrentPid, buf); 126238032Speter if (SevenBitInput) 126338032Speter { 126438032Speter for (p = buf; *p != '\0'; p++) 126538032Speter *p &= ~0200; 126638032Speter } 126738032Speter else if (!HasEightBits) 126838032Speter { 126938032Speter for (p = buf; *p != '\0'; p++) 127038032Speter { 127138032Speter if (bitset(0200, *p)) 127238032Speter { 127390792Sgshapiro HasEightBits = true; 127438032Speter break; 127538032Speter } 127638032Speter } 127738032Speter } 127864562Sgshapiro return buf; 127938032Speter} 128090792Sgshapiro/* 128190792Sgshapiro** FGETFOLDED -- like fgets, but knows about folded lines. 128238032Speter** 128338032Speter** Parameters: 128438032Speter** buf -- place to put result. 128538032Speter** n -- bytes available. 128638032Speter** f -- file to read from. 128738032Speter** 128838032Speter** Returns: 128990792Sgshapiro** input line(s) on success, NULL on error or SM_IO_EOF. 129038032Speter** This will normally be buf -- unless the line is too 129190792Sgshapiro** long, when it will be sm_malloc_x()ed. 129238032Speter** 129338032Speter** Side Effects: 129438032Speter** buf gets lines from f, with continuation lines (lines 129538032Speter** with leading white space) appended. CRLF's are mapped 129638032Speter** into single newlines. Any trailing NL is stripped. 129738032Speter*/ 129838032Speter 129938032Speterchar * 130038032Speterfgetfolded(buf, n, f) 130138032Speter char *buf; 130238032Speter register int n; 130390792Sgshapiro SM_FILE_T *f; 130438032Speter{ 130538032Speter register char *p = buf; 130638032Speter char *bp = buf; 130738032Speter register int i; 130838032Speter 130990792Sgshapiro SM_REQUIRE(n > 0); 131090792Sgshapiro SM_REQUIRE(buf != NULL); 131190792Sgshapiro if (f == NULL) 131290792Sgshapiro { 131390792Sgshapiro buf[0] = '\0'; 131490792Sgshapiro errno = EBADF; 131590792Sgshapiro return NULL; 131690792Sgshapiro } 131790792Sgshapiro 131838032Speter n--; 131990792Sgshapiro while ((i = sm_io_getc(f, SM_TIME_DEFAULT)) != SM_IO_EOF) 132038032Speter { 132138032Speter if (i == '\r') 132238032Speter { 132390792Sgshapiro i = sm_io_getc(f, SM_TIME_DEFAULT); 132438032Speter if (i != '\n') 132538032Speter { 132690792Sgshapiro if (i != SM_IO_EOF) 132790792Sgshapiro (void) sm_io_ungetc(f, SM_TIME_DEFAULT, 132890792Sgshapiro i); 132938032Speter i = '\r'; 133038032Speter } 133138032Speter } 133238032Speter if (--n <= 0) 133338032Speter { 133438032Speter /* allocate new space */ 133538032Speter char *nbp; 133638032Speter int nn; 133738032Speter 133838032Speter nn = (p - bp); 133938032Speter if (nn < MEMCHUNKSIZE) 134038032Speter nn *= 2; 134138032Speter else 134238032Speter nn += MEMCHUNKSIZE; 134390792Sgshapiro nbp = sm_malloc_x(nn); 134464562Sgshapiro memmove(nbp, bp, p - bp); 134538032Speter p = &nbp[p - bp]; 134638032Speter if (bp != buf) 134777349Sgshapiro sm_free(bp); 134838032Speter bp = nbp; 134938032Speter n = nn - (p - bp); 135038032Speter } 135138032Speter *p++ = i; 135238032Speter if (i == '\n') 135338032Speter { 135438032Speter LineNumber++; 135590792Sgshapiro i = sm_io_getc(f, SM_TIME_DEFAULT); 135690792Sgshapiro if (i != SM_IO_EOF) 135790792Sgshapiro (void) sm_io_ungetc(f, SM_TIME_DEFAULT, i); 135838032Speter if (i != ' ' && i != '\t') 135938032Speter break; 136038032Speter } 136138032Speter } 136238032Speter if (p == bp) 136364562Sgshapiro return NULL; 136438032Speter if (p[-1] == '\n') 136538032Speter p--; 136638032Speter *p = '\0'; 136764562Sgshapiro return bp; 136838032Speter} 136990792Sgshapiro/* 137038032Speter** CURTIME -- return current time. 137138032Speter** 137238032Speter** Parameters: 137338032Speter** none. 137438032Speter** 137538032Speter** Returns: 137638032Speter** the current time. 137738032Speter*/ 137838032Speter 137938032Spetertime_t 138038032Spetercurtime() 138138032Speter{ 138238032Speter auto time_t t; 138338032Speter 138438032Speter (void) time(&t); 138564562Sgshapiro return t; 138638032Speter} 138790792Sgshapiro/* 138838032Speter** ATOBOOL -- convert a string representation to boolean. 138938032Speter** 139090792Sgshapiro** Defaults to false 139138032Speter** 139238032Speter** Parameters: 139390792Sgshapiro** s -- string to convert. Takes "tTyY", empty, and NULL as true, 139438032Speter** others as false. 139538032Speter** 139638032Speter** Returns: 139738032Speter** A boolean representation of the string. 139838032Speter*/ 139938032Speter 140038032Speterbool 140138032Speteratobool(s) 140238032Speter register char *s; 140338032Speter{ 140438032Speter if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL) 140590792Sgshapiro return true; 140690792Sgshapiro return false; 140738032Speter} 140890792Sgshapiro/* 140938032Speter** ATOOCT -- convert a string representation to octal. 141038032Speter** 141138032Speter** Parameters: 141238032Speter** s -- string to convert. 141338032Speter** 141438032Speter** Returns: 141538032Speter** An integer representing the string interpreted as an 141638032Speter** octal number. 141738032Speter*/ 141838032Speter 141938032Speterint 142038032Speteratooct(s) 142138032Speter register char *s; 142238032Speter{ 142338032Speter register int i = 0; 142438032Speter 142538032Speter while (*s >= '0' && *s <= '7') 142638032Speter i = (i << 3) | (*s++ - '0'); 142764562Sgshapiro return i; 142838032Speter} 142990792Sgshapiro/* 143038032Speter** BITINTERSECT -- tell if two bitmaps intersect 143138032Speter** 143238032Speter** Parameters: 143338032Speter** a, b -- the bitmaps in question 143438032Speter** 143538032Speter** Returns: 143690792Sgshapiro** true if they have a non-null intersection 143790792Sgshapiro** false otherwise 143838032Speter*/ 143938032Speter 144038032Speterbool 144138032Speterbitintersect(a, b) 144264562Sgshapiro BITMAP256 a; 144364562Sgshapiro BITMAP256 b; 144438032Speter{ 144538032Speter int i; 144638032Speter 144738032Speter for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 144871345Sgshapiro { 144938032Speter if ((a[i] & b[i]) != 0) 145090792Sgshapiro return true; 145171345Sgshapiro } 145290792Sgshapiro return false; 145338032Speter} 145490792Sgshapiro/* 145538032Speter** BITZEROP -- tell if a bitmap is all zero 145638032Speter** 145738032Speter** Parameters: 145838032Speter** map -- the bit map to check 145938032Speter** 146038032Speter** Returns: 146190792Sgshapiro** true if map is all zero. 146290792Sgshapiro** false if there are any bits set in map. 146338032Speter*/ 146438032Speter 146538032Speterbool 146638032Speterbitzerop(map) 146764562Sgshapiro BITMAP256 map; 146838032Speter{ 146938032Speter int i; 147038032Speter 147138032Speter for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 147271345Sgshapiro { 147338032Speter if (map[i] != 0) 147490792Sgshapiro return false; 147571345Sgshapiro } 147690792Sgshapiro return true; 147738032Speter} 147890792Sgshapiro/* 147938032Speter** STRCONTAINEDIN -- tell if one string is contained in another 148038032Speter** 148138032Speter** Parameters: 148290792Sgshapiro** icase -- ignore case? 148338032Speter** a -- possible substring. 148438032Speter** b -- possible superstring. 148538032Speter** 148638032Speter** Returns: 148790792Sgshapiro** true if a is contained in b (case insensitive). 148890792Sgshapiro** false otherwise. 148938032Speter*/ 149038032Speter 149138032Speterbool 149290792Sgshapirostrcontainedin(icase, a, b) 149390792Sgshapiro bool icase; 149438032Speter register char *a; 149538032Speter register char *b; 149638032Speter{ 149738032Speter int la; 149838032Speter int lb; 149938032Speter int c; 150038032Speter 150138032Speter la = strlen(a); 150238032Speter lb = strlen(b); 150338032Speter c = *a; 150490792Sgshapiro if (icase && isascii(c) && isupper(c)) 150538032Speter c = tolower(c); 150638032Speter for (; lb-- >= la; b++) 150738032Speter { 150890792Sgshapiro if (icase) 150990792Sgshapiro { 151090792Sgshapiro if (*b != c && 151190792Sgshapiro isascii(*b) && isupper(*b) && tolower(*b) != c) 151290792Sgshapiro continue; 151390792Sgshapiro if (sm_strncasecmp(a, b, la) == 0) 151490792Sgshapiro return true; 151590792Sgshapiro } 151690792Sgshapiro else 151790792Sgshapiro { 151890792Sgshapiro if (*b != c) 151990792Sgshapiro continue; 152090792Sgshapiro if (strncmp(a, b, la) == 0) 152190792Sgshapiro return true; 152290792Sgshapiro } 152338032Speter } 152490792Sgshapiro return false; 152538032Speter} 152690792Sgshapiro/* 152738032Speter** CHECKFD012 -- check low numbered file descriptors 152838032Speter** 152938032Speter** File descriptors 0, 1, and 2 should be open at all times. 153038032Speter** This routine verifies that, and fixes it if not true. 153138032Speter** 153238032Speter** Parameters: 153338032Speter** where -- a tag printed if the assertion failed 153438032Speter** 153538032Speter** Returns: 153638032Speter** none 153738032Speter*/ 153838032Speter 153938032Spetervoid 154038032Spetercheckfd012(where) 154138032Speter char *where; 154238032Speter{ 154338032Speter#if XDEBUG 154438032Speter register int i; 154538032Speter 154638032Speter for (i = 0; i < 3; i++) 154738032Speter fill_fd(i, where); 154838032Speter#endif /* XDEBUG */ 154938032Speter} 155090792Sgshapiro/* 155138032Speter** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging 155238032Speter** 155338032Speter** Parameters: 155438032Speter** fd -- file descriptor to check. 155538032Speter** where -- tag to print on failure. 155638032Speter** 155738032Speter** Returns: 155838032Speter** none. 155938032Speter*/ 156038032Speter 156138032Spetervoid 156238032Spetercheckfdopen(fd, where) 156338032Speter int fd; 156438032Speter char *where; 156538032Speter{ 156638032Speter#if XDEBUG 156738032Speter struct stat st; 156838032Speter 156938032Speter if (fstat(fd, &st) < 0 && errno == EBADF) 157038032Speter { 157138032Speter syserr("checkfdopen(%d): %s not open as expected!", fd, where); 157290792Sgshapiro printopenfds(true); 157338032Speter } 157464562Sgshapiro#endif /* XDEBUG */ 157538032Speter} 157690792Sgshapiro/* 157738032Speter** CHECKFDS -- check for new or missing file descriptors 157838032Speter** 157938032Speter** Parameters: 158038032Speter** where -- tag for printing. If null, take a base line. 158138032Speter** 158238032Speter** Returns: 158338032Speter** none 158438032Speter** 158538032Speter** Side Effects: 158638032Speter** If where is set, shows changes since the last call. 158738032Speter*/ 158838032Speter 158938032Spetervoid 159038032Spetercheckfds(where) 159138032Speter char *where; 159238032Speter{ 159338032Speter int maxfd; 159438032Speter register int fd; 159590792Sgshapiro bool printhdr = true; 159638032Speter int save_errno = errno; 159764562Sgshapiro static BITMAP256 baseline; 159838032Speter extern int DtableSize; 159938032Speter 160071345Sgshapiro if (DtableSize > BITMAPBITS) 160171345Sgshapiro maxfd = BITMAPBITS; 160238032Speter else 160338032Speter maxfd = DtableSize; 160438032Speter if (where == NULL) 160538032Speter clrbitmap(baseline); 160638032Speter 160738032Speter for (fd = 0; fd < maxfd; fd++) 160838032Speter { 160938032Speter struct stat stbuf; 161038032Speter 161138032Speter if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP) 161238032Speter { 161338032Speter if (!bitnset(fd, baseline)) 161438032Speter continue; 161538032Speter clrbitn(fd, baseline); 161638032Speter } 161738032Speter else if (!bitnset(fd, baseline)) 161838032Speter setbitn(fd, baseline); 161938032Speter else 162038032Speter continue; 162138032Speter 162238032Speter /* file state has changed */ 162338032Speter if (where == NULL) 162438032Speter continue; 162538032Speter if (printhdr) 162638032Speter { 162738032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 162864562Sgshapiro "%s: changed fds:", 162964562Sgshapiro where); 163090792Sgshapiro printhdr = false; 163138032Speter } 163290792Sgshapiro dumpfd(fd, true, true); 163338032Speter } 163438032Speter errno = save_errno; 163538032Speter} 163690792Sgshapiro/* 163738032Speter** PRINTOPENFDS -- print the open file descriptors (for debugging) 163838032Speter** 163938032Speter** Parameters: 164038032Speter** logit -- if set, send output to syslog; otherwise 164138032Speter** print for debugging. 164238032Speter** 164338032Speter** Returns: 164438032Speter** none. 164538032Speter*/ 164638032Speter 164764562Sgshapiro#if NETINET || NETINET6 164864562Sgshapiro# include <arpa/inet.h> 164964562Sgshapiro#endif /* NETINET || NETINET6 */ 165038032Speter 165138032Spetervoid 165238032Speterprintopenfds(logit) 165338032Speter bool logit; 165438032Speter{ 165538032Speter register int fd; 165638032Speter extern int DtableSize; 165738032Speter 165838032Speter for (fd = 0; fd < DtableSize; fd++) 165990792Sgshapiro dumpfd(fd, false, logit); 166038032Speter} 166190792Sgshapiro/* 166238032Speter** DUMPFD -- dump a file descriptor 166338032Speter** 166438032Speter** Parameters: 166538032Speter** fd -- the file descriptor to dump. 166638032Speter** printclosed -- if set, print a notification even if 166738032Speter** it is closed; otherwise print nothing. 166838032Speter** logit -- if set, send output to syslog instead of stdout. 166990792Sgshapiro** 167090792Sgshapiro** Returns: 167190792Sgshapiro** none. 167238032Speter*/ 167338032Speter 167438032Spetervoid 167538032Speterdumpfd(fd, printclosed, logit) 167638032Speter int fd; 167738032Speter bool printclosed; 167838032Speter bool logit; 167938032Speter{ 168038032Speter register char *p; 168138032Speter char *hp; 168238032Speter#ifdef S_IFSOCK 168338032Speter SOCKADDR sa; 168464562Sgshapiro#endif /* S_IFSOCK */ 168538032Speter auto SOCKADDR_LEN_T slen; 168638032Speter int i; 168738032Speter#if STAT64 > 0 168838032Speter struct stat64 st; 168964562Sgshapiro#else /* STAT64 > 0 */ 169038032Speter struct stat st; 169164562Sgshapiro#endif /* STAT64 > 0 */ 169238032Speter char buf[200]; 169338032Speter 169438032Speter p = buf; 169590792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "%3d: ", fd); 169638032Speter p += strlen(p); 169738032Speter 169838032Speter if ( 169938032Speter#if STAT64 > 0 170038032Speter fstat64(fd, &st) 170164562Sgshapiro#else /* STAT64 > 0 */ 170238032Speter fstat(fd, &st) 170364562Sgshapiro#endif /* STAT64 > 0 */ 170438032Speter < 0) 170538032Speter { 170638032Speter if (errno != EBADF) 170738032Speter { 170890792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 170990792Sgshapiro "CANNOT STAT (%s)", 171090792Sgshapiro sm_errstring(errno)); 171138032Speter goto printit; 171238032Speter } 171338032Speter else if (printclosed) 171438032Speter { 171590792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "CLOSED"); 171638032Speter goto printit; 171738032Speter } 171838032Speter return; 171938032Speter } 172038032Speter 172194334Sgshapiro i = fcntl(fd, F_GETFL, 0); 172238032Speter if (i != -1) 172338032Speter { 172490792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i); 172538032Speter p += strlen(p); 172638032Speter } 172738032Speter 172890792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "mode=%o: ", 172990792Sgshapiro (int) st.st_mode); 173038032Speter p += strlen(p); 173138032Speter switch (st.st_mode & S_IFMT) 173238032Speter { 173338032Speter#ifdef S_IFSOCK 173438032Speter case S_IFSOCK: 173590792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "SOCK "); 173638032Speter p += strlen(p); 173764562Sgshapiro memset(&sa, '\0', sizeof sa); 173838032Speter slen = sizeof sa; 173938032Speter if (getsockname(fd, &sa.sa, &slen) < 0) 174090792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)", 174190792Sgshapiro sm_errstring(errno)); 174238032Speter else 174338032Speter { 174438032Speter hp = hostnamebyanyaddr(&sa); 174564562Sgshapiro if (hp == NULL) 174664562Sgshapiro { 174764562Sgshapiro /* EMPTY */ 174864562Sgshapiro /* do nothing */ 174964562Sgshapiro } 175064562Sgshapiro# if NETINET 175164562Sgshapiro else if (sa.sa.sa_family == AF_INET) 175290792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 175390792Sgshapiro "%s/%d", hp, ntohs(sa.sin.sin_port)); 175464562Sgshapiro# endif /* NETINET */ 175564562Sgshapiro# if NETINET6 175664562Sgshapiro else if (sa.sa.sa_family == AF_INET6) 175790792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 175890792Sgshapiro "%s/%d", hp, ntohs(sa.sin6.sin6_port)); 175964562Sgshapiro# endif /* NETINET6 */ 176038032Speter else 176190792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 176290792Sgshapiro "%s", hp); 176338032Speter } 176438032Speter p += strlen(p); 176590792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "->"); 176638032Speter p += strlen(p); 176738032Speter slen = sizeof sa; 176838032Speter if (getpeername(fd, &sa.sa, &slen) < 0) 176990792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)", 177090792Sgshapiro sm_errstring(errno)); 177138032Speter else 177238032Speter { 177338032Speter hp = hostnamebyanyaddr(&sa); 177464562Sgshapiro if (hp == NULL) 177564562Sgshapiro { 177664562Sgshapiro /* EMPTY */ 177764562Sgshapiro /* do nothing */ 177864562Sgshapiro } 177964562Sgshapiro# if NETINET 178064562Sgshapiro else if (sa.sa.sa_family == AF_INET) 178190792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 178290792Sgshapiro "%s/%d", hp, ntohs(sa.sin.sin_port)); 178364562Sgshapiro# endif /* NETINET */ 178464562Sgshapiro# if NETINET6 178564562Sgshapiro else if (sa.sa.sa_family == AF_INET6) 178690792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 178790792Sgshapiro "%s/%d", hp, ntohs(sa.sin6.sin6_port)); 178864562Sgshapiro# endif /* NETINET6 */ 178938032Speter else 179090792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 179190792Sgshapiro "%s", hp); 179238032Speter } 179338032Speter break; 179464562Sgshapiro#endif /* S_IFSOCK */ 179538032Speter 179638032Speter case S_IFCHR: 179790792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "CHR: "); 179838032Speter p += strlen(p); 179938032Speter goto defprint; 180038032Speter 180190792Sgshapiro#ifdef S_IFBLK 180238032Speter case S_IFBLK: 180390792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "BLK: "); 180438032Speter p += strlen(p); 180538032Speter goto defprint; 180690792Sgshapiro#endif /* S_IFBLK */ 180738032Speter 180838032Speter#if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) 180938032Speter case S_IFIFO: 181090792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "FIFO: "); 181138032Speter p += strlen(p); 181238032Speter goto defprint; 181364562Sgshapiro#endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */ 181438032Speter 181538032Speter#ifdef S_IFDIR 181638032Speter case S_IFDIR: 181790792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "DIR: "); 181838032Speter p += strlen(p); 181938032Speter goto defprint; 182064562Sgshapiro#endif /* S_IFDIR */ 182138032Speter 182238032Speter#ifdef S_IFLNK 182338032Speter case S_IFLNK: 182490792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "LNK: "); 182538032Speter p += strlen(p); 182638032Speter goto defprint; 182764562Sgshapiro#endif /* S_IFLNK */ 182838032Speter 182938032Speter default: 183038032Speterdefprint: 183190792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 183290792Sgshapiro "dev=%d/%d, ino=%llu, nlink=%d, u/gid=%d/%d, ", 183390792Sgshapiro major(st.st_dev), minor(st.st_dev), 183490792Sgshapiro (ULONGLONG_T) st.st_ino, 183590792Sgshapiro (int) st.st_nlink, (int) st.st_uid, 183690792Sgshapiro (int) st.st_gid); 183790792Sgshapiro p += strlen(p); 183890792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "size=%llu", 183990792Sgshapiro (ULONGLONG_T) st.st_size); 184038032Speter break; 184138032Speter } 184238032Speter 184338032Speterprintit: 184438032Speter if (logit) 184538032Speter sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL, 184664562Sgshapiro "%.800s", buf); 184738032Speter else 184890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", buf); 184938032Speter} 185090792Sgshapiro/* 185138032Speter** SHORTEN_HOSTNAME -- strip local domain information off of hostname. 185238032Speter** 185338032Speter** Parameters: 185438032Speter** host -- the host to shorten (stripped in place). 185538032Speter** 185638032Speter** Returns: 185790792Sgshapiro** place where string was truncated, NULL if not truncated. 185838032Speter*/ 185938032Speter 186073188Sgshapirochar * 186138032Spetershorten_hostname(host) 186238032Speter char host[]; 186338032Speter{ 186438032Speter register char *p; 186538032Speter char *mydom; 186638032Speter int i; 186790792Sgshapiro bool canon = false; 186838032Speter 186938032Speter /* strip off final dot */ 187090792Sgshapiro i = strlen(host); 187190792Sgshapiro p = &host[(i == 0) ? 0 : i - 1]; 187238032Speter if (*p == '.') 187338032Speter { 187438032Speter *p = '\0'; 187590792Sgshapiro canon = true; 187638032Speter } 187738032Speter 187838032Speter /* see if there is any domain at all -- if not, we are done */ 187938032Speter p = strchr(host, '.'); 188038032Speter if (p == NULL) 188173188Sgshapiro return NULL; 188238032Speter 188338032Speter /* yes, we have a domain -- see if it looks like us */ 188438032Speter mydom = macvalue('m', CurEnv); 188538032Speter if (mydom == NULL) 188638032Speter mydom = ""; 188738032Speter i = strlen(++p); 188890792Sgshapiro if ((canon ? sm_strcasecmp(p, mydom) 188990792Sgshapiro : sm_strncasecmp(p, mydom, i)) == 0 && 189090792Sgshapiro (mydom[i] == '.' || mydom[i] == '\0')) 189173188Sgshapiro { 189238032Speter *--p = '\0'; 189373188Sgshapiro return p; 189473188Sgshapiro } 189573188Sgshapiro return NULL; 189638032Speter} 189790792Sgshapiro/* 189838032Speter** PROG_OPEN -- open a program for reading 189938032Speter** 190038032Speter** Parameters: 190138032Speter** argv -- the argument list. 190238032Speter** pfd -- pointer to a place to store the file descriptor. 190338032Speter** e -- the current envelope. 190438032Speter** 190538032Speter** Returns: 190638032Speter** pid of the process -- -1 if it failed. 190738032Speter*/ 190838032Speter 190977349Sgshapiropid_t 191038032Speterprog_open(argv, pfd, e) 191138032Speter char **argv; 191238032Speter int *pfd; 191338032Speter ENVELOPE *e; 191438032Speter{ 191577349Sgshapiro pid_t pid; 191638032Speter int i; 191764562Sgshapiro int save_errno; 191890792Sgshapiro int sff; 191990792Sgshapiro int ret; 192038032Speter int fdv[2]; 192138032Speter char *p, *q; 192298121Sgshapiro char buf[MAXPATHLEN]; 192338032Speter extern int DtableSize; 192438032Speter 192538032Speter if (pipe(fdv) < 0) 192638032Speter { 192738032Speter syserr("%s: cannot create pipe for stdout", argv[0]); 192838032Speter return -1; 192938032Speter } 193038032Speter pid = fork(); 193138032Speter if (pid < 0) 193238032Speter { 193338032Speter syserr("%s: cannot fork", argv[0]); 193464562Sgshapiro (void) close(fdv[0]); 193564562Sgshapiro (void) close(fdv[1]); 193638032Speter return -1; 193738032Speter } 193838032Speter if (pid > 0) 193938032Speter { 194038032Speter /* parent */ 194164562Sgshapiro (void) close(fdv[1]); 194238032Speter *pfd = fdv[0]; 194338032Speter return pid; 194438032Speter } 194538032Speter 194677349Sgshapiro /* Reset global flags */ 194777349Sgshapiro RestartRequest = NULL; 194890792Sgshapiro RestartWorkGroup = false; 194977349Sgshapiro ShutdownRequest = NULL; 195077349Sgshapiro PendingSignal = 0; 195190792Sgshapiro CurrentPid = getpid(); 195277349Sgshapiro 195390792Sgshapiro /* 195490792Sgshapiro ** Initialize exception stack and default exception 195590792Sgshapiro ** handler for child process. 195690792Sgshapiro */ 195790792Sgshapiro 195890792Sgshapiro sm_exc_newthread(fatal_error); 195990792Sgshapiro 196090792Sgshapiro /* child -- close stdin */ 196190792Sgshapiro (void) close(0); 196290792Sgshapiro 196338032Speter /* stdout goes back to parent */ 196464562Sgshapiro (void) close(fdv[0]); 196538032Speter if (dup2(fdv[1], 1) < 0) 196638032Speter { 196738032Speter syserr("%s: cannot dup2 for stdout", argv[0]); 196838032Speter _exit(EX_OSERR); 196938032Speter } 197064562Sgshapiro (void) close(fdv[1]); 197138032Speter 197238032Speter /* stderr goes to transcript if available */ 197338032Speter if (e->e_xfp != NULL) 197438032Speter { 197564562Sgshapiro int xfd; 197664562Sgshapiro 197790792Sgshapiro xfd = sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL); 197864562Sgshapiro if (xfd >= 0 && dup2(xfd, 2) < 0) 197938032Speter { 198038032Speter syserr("%s: cannot dup2 for stderr", argv[0]); 198138032Speter _exit(EX_OSERR); 198238032Speter } 198338032Speter } 198438032Speter 198538032Speter /* this process has no right to the queue file */ 198638032Speter if (e->e_lockfp != NULL) 198790792Sgshapiro (void) close(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL)); 198838032Speter 198964562Sgshapiro /* chroot to the program mailer directory, if defined */ 199064562Sgshapiro if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL) 199164562Sgshapiro { 199264562Sgshapiro expand(ProgMailer->m_rootdir, buf, sizeof buf, e); 199364562Sgshapiro if (chroot(buf) < 0) 199464562Sgshapiro { 199564562Sgshapiro syserr("prog_open: cannot chroot(%s)", buf); 199664562Sgshapiro exit(EX_TEMPFAIL); 199764562Sgshapiro } 199864562Sgshapiro if (chdir("/") < 0) 199964562Sgshapiro { 200064562Sgshapiro syserr("prog_open: cannot chdir(/)"); 200164562Sgshapiro exit(EX_TEMPFAIL); 200264562Sgshapiro } 200364562Sgshapiro } 200464562Sgshapiro 200538032Speter /* run as default user */ 200638032Speter endpwent(); 200790792Sgshapiro sm_mbdb_terminate(); 200838032Speter if (setgid(DefGid) < 0 && geteuid() == 0) 200964562Sgshapiro { 201038032Speter syserr("prog_open: setgid(%ld) failed", (long) DefGid); 201164562Sgshapiro exit(EX_TEMPFAIL); 201264562Sgshapiro } 201338032Speter if (setuid(DefUid) < 0 && geteuid() == 0) 201464562Sgshapiro { 201538032Speter syserr("prog_open: setuid(%ld) failed", (long) DefUid); 201664562Sgshapiro exit(EX_TEMPFAIL); 201764562Sgshapiro } 201838032Speter 201938032Speter /* run in some directory */ 202038032Speter if (ProgMailer != NULL) 202138032Speter p = ProgMailer->m_execdir; 202238032Speter else 202338032Speter p = NULL; 202438032Speter for (; p != NULL; p = q) 202538032Speter { 202638032Speter q = strchr(p, ':'); 202738032Speter if (q != NULL) 202838032Speter *q = '\0'; 202938032Speter expand(p, buf, sizeof buf, e); 203038032Speter if (q != NULL) 203138032Speter *q++ = ':'; 203238032Speter if (buf[0] != '\0' && chdir(buf) >= 0) 203338032Speter break; 203438032Speter } 203538032Speter if (p == NULL) 203638032Speter { 203738032Speter /* backup directories */ 203838032Speter if (chdir("/tmp") < 0) 203938032Speter (void) chdir("/"); 204038032Speter } 204138032Speter 204290792Sgshapiro /* Check safety of program to be run */ 204390792Sgshapiro sff = SFF_ROOTOK|SFF_EXECOK; 204490792Sgshapiro if (!bitnset(DBS_RUNWRITABLEPROGRAM, DontBlameSendmail)) 204590792Sgshapiro sff |= SFF_NOGWFILES|SFF_NOWWFILES; 204690792Sgshapiro if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, DontBlameSendmail)) 204790792Sgshapiro sff |= SFF_NOPATHCHECK; 204890792Sgshapiro else 204990792Sgshapiro sff |= SFF_SAFEDIRPATH; 205090792Sgshapiro ret = safefile(argv[0], DefUid, DefGid, DefUser, sff, 0, NULL); 205190792Sgshapiro if (ret != 0) 205290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 205390792Sgshapiro "Warning: prog_open: program %s unsafe: %s", 205490792Sgshapiro argv[0], sm_errstring(ret)); 205590792Sgshapiro 205638032Speter /* arrange for all the files to be closed */ 205738032Speter for (i = 3; i < DtableSize; i++) 205838032Speter { 205938032Speter register int j; 206038032Speter 206138032Speter if ((j = fcntl(i, F_GETFD, 0)) != -1) 206264562Sgshapiro (void) fcntl(i, F_SETFD, j | FD_CLOEXEC); 206338032Speter } 206438032Speter 206538032Speter /* now exec the process */ 206664562Sgshapiro (void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron); 206738032Speter 206838032Speter /* woops! failed */ 206964562Sgshapiro save_errno = errno; 207038032Speter syserr("%s: cannot exec", argv[0]); 207164562Sgshapiro if (transienterror(save_errno)) 207238032Speter _exit(EX_OSERR); 207338032Speter _exit(EX_CONFIG); 207438032Speter return -1; /* avoid compiler warning on IRIX */ 207538032Speter} 207690792Sgshapiro/* 207764562Sgshapiro** GET_COLUMN -- look up a Column in a line buffer 207838032Speter** 207938032Speter** Parameters: 208038032Speter** line -- the raw text line to search. 208138032Speter** col -- the column number to fetch. 208238032Speter** delim -- the delimiter between columns. If null, 208338032Speter** use white space. 208438032Speter** buf -- the output buffer. 208538032Speter** buflen -- the length of buf. 208638032Speter** 208738032Speter** Returns: 208838032Speter** buf if successful. 208938032Speter** NULL otherwise. 209038032Speter*/ 209138032Speter 209238032Speterchar * 209338032Speterget_column(line, col, delim, buf, buflen) 209438032Speter char line[]; 209538032Speter int col; 209664562Sgshapiro int delim; 209738032Speter char buf[]; 209838032Speter int buflen; 209938032Speter{ 210038032Speter char *p; 210138032Speter char *begin, *end; 210238032Speter int i; 210338032Speter char delimbuf[4]; 210464562Sgshapiro 210590792Sgshapiro if ((char) delim == '\0') 210690792Sgshapiro (void) sm_strlcpy(delimbuf, "\n\t ", sizeof delimbuf); 210738032Speter else 210838032Speter { 210990792Sgshapiro delimbuf[0] = (char) delim; 211038032Speter delimbuf[1] = '\0'; 211138032Speter } 211238032Speter 211338032Speter p = line; 211438032Speter if (*p == '\0') 211538032Speter return NULL; /* line empty */ 211690792Sgshapiro if (*p == (char) delim && col == 0) 211738032Speter return NULL; /* first column empty */ 211838032Speter 211938032Speter begin = line; 212038032Speter 212190792Sgshapiro if (col == 0 && (char) delim == '\0') 212238032Speter { 212338032Speter while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 212438032Speter begin++; 212538032Speter } 212638032Speter 212738032Speter for (i = 0; i < col; i++) 212838032Speter { 212938032Speter if ((begin = strpbrk(begin, delimbuf)) == NULL) 213038032Speter return NULL; /* no such column */ 213138032Speter begin++; 213290792Sgshapiro if ((char) delim == '\0') 213338032Speter { 213438032Speter while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 213538032Speter begin++; 213638032Speter } 213738032Speter } 213864562Sgshapiro 213938032Speter end = strpbrk(begin, delimbuf); 214038032Speter if (end == NULL) 214138032Speter i = strlen(begin); 214238032Speter else 214338032Speter i = end - begin; 214438032Speter if (i >= buflen) 214538032Speter i = buflen - 1; 214690792Sgshapiro (void) sm_strlcpy(buf, begin, i + 1); 214738032Speter return buf; 214838032Speter} 214990792Sgshapiro/* 215038032Speter** CLEANSTRCPY -- copy string keeping out bogus characters 215138032Speter** 215238032Speter** Parameters: 215338032Speter** t -- "to" string. 215438032Speter** f -- "from" string. 215538032Speter** l -- length of space available in "to" string. 215638032Speter** 215738032Speter** Returns: 215838032Speter** none. 215938032Speter*/ 216038032Speter 216138032Spetervoid 216238032Spetercleanstrcpy(t, f, l) 216338032Speter register char *t; 216438032Speter register char *f; 216538032Speter int l; 216638032Speter{ 216738032Speter /* check for newlines and log if necessary */ 216890792Sgshapiro (void) denlstring(f, true, true); 216938032Speter 217064562Sgshapiro if (l <= 0) 217164562Sgshapiro syserr("!cleanstrcpy: length == 0"); 217264562Sgshapiro 217338032Speter l--; 217438032Speter while (l > 0 && *f != '\0') 217538032Speter { 217638032Speter if (isascii(*f) && 217738032Speter (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL)) 217838032Speter { 217938032Speter l--; 218038032Speter *t++ = *f; 218138032Speter } 218238032Speter f++; 218338032Speter } 218438032Speter *t = '\0'; 218538032Speter} 218690792Sgshapiro/* 218738032Speter** DENLSTRING -- convert newlines in a string to spaces 218838032Speter** 218938032Speter** Parameters: 219038032Speter** s -- the input string 219138032Speter** strict -- if set, don't permit continuation lines. 219238032Speter** logattacks -- if set, log attempted attacks. 219338032Speter** 219438032Speter** Returns: 219538032Speter** A pointer to a version of the string with newlines 219638032Speter** mapped to spaces. This should be copied. 219738032Speter*/ 219838032Speter 219938032Speterchar * 220038032Speterdenlstring(s, strict, logattacks) 220138032Speter char *s; 220238032Speter bool strict; 220338032Speter bool logattacks; 220438032Speter{ 220538032Speter register char *p; 220638032Speter int l; 220738032Speter static char *bp = NULL; 220838032Speter static int bl = 0; 220938032Speter 221038032Speter p = s; 221138032Speter while ((p = strchr(p, '\n')) != NULL) 221238032Speter if (strict || (*++p != ' ' && *p != '\t')) 221338032Speter break; 221438032Speter if (p == NULL) 221538032Speter return s; 221638032Speter 221738032Speter l = strlen(s) + 1; 221838032Speter if (bl < l) 221938032Speter { 222038032Speter /* allocate more space */ 222190792Sgshapiro char *nbp = sm_pmalloc_x(l); 222290792Sgshapiro 222338032Speter if (bp != NULL) 222477349Sgshapiro sm_free(bp); 222590792Sgshapiro bp = nbp; 222638032Speter bl = l; 222738032Speter } 222890792Sgshapiro (void) sm_strlcpy(bp, s, l); 222938032Speter for (p = bp; (p = strchr(p, '\n')) != NULL; ) 223038032Speter *p++ = ' '; 223138032Speter 223238032Speter if (logattacks) 223338032Speter { 223438032Speter sm_syslog(LOG_NOTICE, CurEnv->e_id, 223564562Sgshapiro "POSSIBLE ATTACK from %.100s: newline in string \"%s\"", 223664562Sgshapiro RealHostName == NULL ? "[UNKNOWN]" : RealHostName, 223764562Sgshapiro shortenstring(bp, MAXSHORTSTR)); 223838032Speter } 223938032Speter 224038032Speter return bp; 224138032Speter} 224298841Sgshapiro 224390792Sgshapiro/* 224498841Sgshapiro** STRREPLNONPRT -- replace "unprintable" characters in a string with subst 224598841Sgshapiro** 224698841Sgshapiro** Parameters: 224798841Sgshapiro** s -- string to manipulate (in place) 224898841Sgshapiro** subst -- character to use as replacement 224998841Sgshapiro** 225098841Sgshapiro** Returns: 225198841Sgshapiro** true iff string did not contain "unprintable" characters 225298841Sgshapiro*/ 225398841Sgshapiro 225498841Sgshapirobool 225598841Sgshapirostrreplnonprt(s, c) 225698841Sgshapiro char *s; 225798841Sgshapiro int c; 225898841Sgshapiro{ 225998841Sgshapiro bool ok; 226098841Sgshapiro 226198841Sgshapiro ok = true; 226298841Sgshapiro if (s == NULL) 226398841Sgshapiro return ok; 226498841Sgshapiro while (*s != '\0') 226598841Sgshapiro { 226698841Sgshapiro if (!(isascii(*s) && isprint(*s))) 226798841Sgshapiro { 226898841Sgshapiro *s = c; 226998841Sgshapiro ok = false; 227098841Sgshapiro } 227198841Sgshapiro ++s; 227298841Sgshapiro } 227398841Sgshapiro return ok; 227498841Sgshapiro} 227598841Sgshapiro 227698841Sgshapiro/* 227790792Sgshapiro** STR2PRT -- convert "unprintable" characters in a string to \oct 227890792Sgshapiro** 227990792Sgshapiro** Parameters: 228090792Sgshapiro** s -- string to convert 228190792Sgshapiro** 228290792Sgshapiro** Returns: 228390792Sgshapiro** converted string. 228490792Sgshapiro** This is a static local buffer, string must be copied 228590792Sgshapiro** before this function is called again! 228690792Sgshapiro*/ 228790792Sgshapiro 228890792Sgshapirochar * 228990792Sgshapirostr2prt(s) 229090792Sgshapiro char *s; 229190792Sgshapiro{ 229290792Sgshapiro int l; 229390792Sgshapiro char c, *h; 229490792Sgshapiro bool ok; 229590792Sgshapiro static int len = 0; 229690792Sgshapiro static char *buf = NULL; 229790792Sgshapiro 229890792Sgshapiro if (s == NULL) 229990792Sgshapiro return NULL; 230090792Sgshapiro ok = true; 230190792Sgshapiro for (h = s, l = 1; *h != '\0'; h++, l++) 230290792Sgshapiro { 230390792Sgshapiro if (*h == '\\') 230490792Sgshapiro { 230590792Sgshapiro ++l; 230690792Sgshapiro ok = false; 230790792Sgshapiro } 230890792Sgshapiro else if (!(isascii(*h) && isprint(*h))) 230990792Sgshapiro { 231090792Sgshapiro l += 3; 231190792Sgshapiro ok = false; 231290792Sgshapiro } 231390792Sgshapiro } 231490792Sgshapiro if (ok) 231590792Sgshapiro return s; 231690792Sgshapiro if (l > len) 231790792Sgshapiro { 231890792Sgshapiro char *nbuf = sm_pmalloc_x(l); 231990792Sgshapiro 232090792Sgshapiro if (buf != NULL) 232190792Sgshapiro sm_free(buf); 232290792Sgshapiro len = l; 232390792Sgshapiro buf = nbuf; 232490792Sgshapiro } 232590792Sgshapiro for (h = buf; *s != '\0' && l > 0; s++, l--) 232690792Sgshapiro { 232790792Sgshapiro c = *s; 232890792Sgshapiro if (isascii(c) && isprint(c) && c != '\\') 232990792Sgshapiro { 233090792Sgshapiro *h++ = c; 233190792Sgshapiro } 233290792Sgshapiro else 233390792Sgshapiro { 233490792Sgshapiro *h++ = '\\'; 233590792Sgshapiro --l; 233690792Sgshapiro switch (c) 233790792Sgshapiro { 233890792Sgshapiro case '\\': 233990792Sgshapiro *h++ = '\\'; 234090792Sgshapiro break; 234190792Sgshapiro case '\t': 234290792Sgshapiro *h++ = 't'; 234390792Sgshapiro break; 234490792Sgshapiro case '\n': 234590792Sgshapiro *h++ = 'n'; 234690792Sgshapiro break; 234790792Sgshapiro case '\r': 234890792Sgshapiro *h++ = 'r'; 234990792Sgshapiro break; 235090792Sgshapiro default: 235190792Sgshapiro (void) sm_snprintf(h, l, "%03o", (int) c); 235290792Sgshapiro 235390792Sgshapiro /* 235490792Sgshapiro ** XXX since l is unsigned this may 235590792Sgshapiro ** wrap around if the calculation is screwed 235690792Sgshapiro ** up... 235790792Sgshapiro */ 235890792Sgshapiro 235990792Sgshapiro l -= 2; 236090792Sgshapiro h += 3; 236190792Sgshapiro break; 236290792Sgshapiro } 236390792Sgshapiro } 236490792Sgshapiro } 236590792Sgshapiro *h = '\0'; 236690792Sgshapiro buf[len - 1] = '\0'; 236790792Sgshapiro return buf; 236890792Sgshapiro} 236990792Sgshapiro/* 237038032Speter** PATH_IS_DIR -- check to see if file exists and is a directory. 237138032Speter** 237238032Speter** There are some additional checks for security violations in 237338032Speter** here. This routine is intended to be used for the host status 237438032Speter** support. 237538032Speter** 237638032Speter** Parameters: 237738032Speter** pathname -- pathname to check for directory-ness. 237838032Speter** createflag -- if set, create directory if needed. 237938032Speter** 238038032Speter** Returns: 238190792Sgshapiro** true -- if the indicated pathname is a directory 238290792Sgshapiro** false -- otherwise 238338032Speter*/ 238438032Speter 238538032Speterint 238638032Speterpath_is_dir(pathname, createflag) 238738032Speter char *pathname; 238838032Speter bool createflag; 238938032Speter{ 239038032Speter struct stat statbuf; 239138032Speter 239238032Speter#if HASLSTAT 239338032Speter if (lstat(pathname, &statbuf) < 0) 239464562Sgshapiro#else /* HASLSTAT */ 239538032Speter if (stat(pathname, &statbuf) < 0) 239664562Sgshapiro#endif /* HASLSTAT */ 239738032Speter { 239838032Speter if (errno != ENOENT || !createflag) 239990792Sgshapiro return false; 240038032Speter if (mkdir(pathname, 0755) < 0) 240190792Sgshapiro return false; 240290792Sgshapiro return true; 240338032Speter } 240438032Speter if (!S_ISDIR(statbuf.st_mode)) 240538032Speter { 240638032Speter errno = ENOTDIR; 240790792Sgshapiro return false; 240838032Speter } 240938032Speter 241038032Speter /* security: don't allow writable directories */ 241138032Speter if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode)) 241238032Speter { 241338032Speter errno = EACCES; 241490792Sgshapiro return false; 241538032Speter } 241690792Sgshapiro return true; 241738032Speter} 241890792Sgshapiro/* 241938032Speter** PROC_LIST_ADD -- add process id to list of our children 242038032Speter** 242138032Speter** Parameters: 242238032Speter** pid -- pid to add to list. 242364562Sgshapiro** task -- task of pid. 242464562Sgshapiro** type -- type of process. 242590792Sgshapiro** count -- number of processes. 242690792Sgshapiro** other -- other information for this type. 242738032Speter** 242838032Speter** Returns: 242938032Speter** none 243090792Sgshapiro** 243190792Sgshapiro** Side Effects: 243290792Sgshapiro** May increase CurChildren. May grow ProcList. 243338032Speter*/ 243438032Speter 243590792Sgshapirotypedef struct procs PROCS_T; 243642575Speter 243790792Sgshapirostruct procs 243890792Sgshapiro{ 243990792Sgshapiro pid_t proc_pid; 244090792Sgshapiro char *proc_task; 244190792Sgshapiro int proc_type; 244290792Sgshapiro int proc_count; 244390792Sgshapiro int proc_other; 244490792Sgshapiro}; 244590792Sgshapiro 244690792Sgshapirostatic PROCS_T *volatile ProcListVec = NULL; 244790792Sgshapirostatic int ProcListSize = 0; 244890792Sgshapiro 244938032Spetervoid 245090792Sgshapiroproc_list_add(pid, task, type, count, other) 245138032Speter pid_t pid; 245242575Speter char *task; 245364562Sgshapiro int type; 245490792Sgshapiro int count; 245590792Sgshapiro int other; 245638032Speter{ 245738032Speter int i; 245838032Speter 245938032Speter for (i = 0; i < ProcListSize; i++) 246038032Speter { 246142575Speter if (ProcListVec[i].proc_pid == NO_PID) 246238032Speter break; 246338032Speter } 246438032Speter if (i >= ProcListSize) 246538032Speter { 246638032Speter /* probe the existing vector to avoid growing infinitely */ 246738032Speter proc_list_probe(); 246838032Speter 246938032Speter /* now scan again */ 247038032Speter for (i = 0; i < ProcListSize; i++) 247138032Speter { 247242575Speter if (ProcListVec[i].proc_pid == NO_PID) 247338032Speter break; 247438032Speter } 247538032Speter } 247638032Speter if (i >= ProcListSize) 247738032Speter { 247838032Speter /* grow process list */ 247990792Sgshapiro PROCS_T *npv; 248038032Speter 248190792Sgshapiro SM_ASSERT(ProcListSize < INT_MAX - PROC_LIST_SEG); 248290792Sgshapiro npv = (PROCS_T *) sm_pmalloc_x((sizeof *npv) * 248390792Sgshapiro (ProcListSize + PROC_LIST_SEG)); 248438032Speter if (ProcListSize > 0) 248538032Speter { 248664562Sgshapiro memmove(npv, ProcListVec, 248790792Sgshapiro ProcListSize * sizeof (PROCS_T)); 248877349Sgshapiro sm_free(ProcListVec); 248938032Speter } 249090792Sgshapiro 249190792Sgshapiro /* XXX just use memset() to initialize this part? */ 249238032Speter for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++) 249342575Speter { 249442575Speter npv[i].proc_pid = NO_PID; 249542575Speter npv[i].proc_task = NULL; 249664562Sgshapiro npv[i].proc_type = PROC_NONE; 249742575Speter } 249838032Speter i = ProcListSize; 249938032Speter ProcListSize += PROC_LIST_SEG; 250038032Speter ProcListVec = npv; 250138032Speter } 250242575Speter ProcListVec[i].proc_pid = pid; 250390792Sgshapiro PSTRSET(ProcListVec[i].proc_task, task); 250464562Sgshapiro ProcListVec[i].proc_type = type; 250590792Sgshapiro ProcListVec[i].proc_count = count; 250690792Sgshapiro ProcListVec[i].proc_other = other; 250742575Speter 250842575Speter /* if process adding itself, it's not a child */ 250990792Sgshapiro if (pid != CurrentPid) 251090792Sgshapiro { 251190792Sgshapiro SM_ASSERT(CurChildren < INT_MAX); 251242575Speter CurChildren++; 251390792Sgshapiro } 251438032Speter} 251590792Sgshapiro/* 251642575Speter** PROC_LIST_SET -- set pid task in process list 251742575Speter** 251842575Speter** Parameters: 251942575Speter** pid -- pid to set 252042575Speter** task -- task of pid 252142575Speter** 252242575Speter** Returns: 252342575Speter** none. 252442575Speter*/ 252542575Speter 252642575Spetervoid 252742575Speterproc_list_set(pid, task) 252842575Speter pid_t pid; 252942575Speter char *task; 253042575Speter{ 253142575Speter int i; 253242575Speter 253342575Speter for (i = 0; i < ProcListSize; i++) 253442575Speter { 253542575Speter if (ProcListVec[i].proc_pid == pid) 253642575Speter { 253790792Sgshapiro PSTRSET(ProcListVec[i].proc_task, task); 253842575Speter break; 253942575Speter } 254042575Speter } 254142575Speter} 254290792Sgshapiro/* 254338032Speter** PROC_LIST_DROP -- drop pid from process list 254438032Speter** 254538032Speter** Parameters: 254638032Speter** pid -- pid to drop 254790792Sgshapiro** st -- process status 254890792Sgshapiro** other -- storage for proc_other (return). 254938032Speter** 255038032Speter** Returns: 255190792Sgshapiro** none. 255277349Sgshapiro** 255390792Sgshapiro** Side Effects: 255490792Sgshapiro** May decrease CurChildren, CurRunners, or 255590792Sgshapiro** set RestartRequest or ShutdownRequest. 255690792Sgshapiro** 255777349Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 255877349Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 255977349Sgshapiro** DOING. 256038032Speter*/ 256138032Speter 256290792Sgshapirovoid 256390792Sgshapiroproc_list_drop(pid, st, other) 256438032Speter pid_t pid; 256590792Sgshapiro int st; 256690792Sgshapiro int *other; 256738032Speter{ 256838032Speter int i; 256964562Sgshapiro int type = PROC_NONE; 257038032Speter 257138032Speter for (i = 0; i < ProcListSize; i++) 257238032Speter { 257342575Speter if (ProcListVec[i].proc_pid == pid) 257438032Speter { 257542575Speter ProcListVec[i].proc_pid = NO_PID; 257664562Sgshapiro type = ProcListVec[i].proc_type; 257790792Sgshapiro if (other != NULL) 257890792Sgshapiro *other = ProcListVec[i].proc_other; 257938032Speter break; 258038032Speter } 258138032Speter } 258238032Speter if (CurChildren > 0) 258338032Speter CurChildren--; 258464562Sgshapiro 258564562Sgshapiro 258690792Sgshapiro if (type == PROC_CONTROL && WIFEXITED(st)) 258790792Sgshapiro { 258890792Sgshapiro /* if so, see if we need to restart or shutdown */ 258990792Sgshapiro if (WEXITSTATUS(st) == EX_RESTART) 259090792Sgshapiro RestartRequest = "control socket"; 259190792Sgshapiro else if (WEXITSTATUS(st) == EX_SHUTDOWN) 259290792Sgshapiro ShutdownRequest = "control socket"; 259390792Sgshapiro } 259490792Sgshapiro else if (type == PROC_QUEUE_CHILD && !WIFSTOPPED(st) && 259590792Sgshapiro ProcListVec[i].proc_other > -1) 259690792Sgshapiro { 259790792Sgshapiro /* restart this persistent runner */ 259890792Sgshapiro mark_work_group_restart(ProcListVec[i].proc_other, st); 259990792Sgshapiro } 260090792Sgshapiro else if (type == PROC_QUEUE) 260190792Sgshapiro CurRunners -= ProcListVec[i].proc_count; 260238032Speter} 260390792Sgshapiro/* 260438032Speter** PROC_LIST_CLEAR -- clear the process list 260538032Speter** 260638032Speter** Parameters: 260738032Speter** none. 260838032Speter** 260938032Speter** Returns: 261038032Speter** none. 261190792Sgshapiro** 261290792Sgshapiro** Side Effects: 261390792Sgshapiro** Sets CurChildren to zero. 261438032Speter*/ 261538032Speter 261638032Spetervoid 261738032Speterproc_list_clear() 261838032Speter{ 261938032Speter int i; 262038032Speter 262142575Speter /* start from 1 since 0 is the daemon itself */ 262242575Speter for (i = 1; i < ProcListSize; i++) 262342575Speter ProcListVec[i].proc_pid = NO_PID; 262438032Speter CurChildren = 0; 262538032Speter} 262690792Sgshapiro/* 262738032Speter** PROC_LIST_PROBE -- probe processes in the list to see if they still exist 262838032Speter** 262938032Speter** Parameters: 263038032Speter** none 263138032Speter** 263238032Speter** Returns: 263338032Speter** none 263490792Sgshapiro** 263590792Sgshapiro** Side Effects: 263690792Sgshapiro** May decrease CurChildren. 263738032Speter*/ 263838032Speter 263938032Spetervoid 264038032Speterproc_list_probe() 264138032Speter{ 264238032Speter int i; 264338032Speter 264442575Speter /* start from 1 since 0 is the daemon itself */ 264542575Speter for (i = 1; i < ProcListSize; i++) 264638032Speter { 264742575Speter if (ProcListVec[i].proc_pid == NO_PID) 264838032Speter continue; 264942575Speter if (kill(ProcListVec[i].proc_pid, 0) < 0) 265038032Speter { 265138032Speter if (LogLevel > 3) 265238032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 265364562Sgshapiro "proc_list_probe: lost pid %d", 265464562Sgshapiro (int) ProcListVec[i].proc_pid); 265542575Speter ProcListVec[i].proc_pid = NO_PID; 265690792Sgshapiro SM_FREE_CLR(ProcListVec[i].proc_task); 265738032Speter CurChildren--; 265838032Speter } 265938032Speter } 266038032Speter if (CurChildren < 0) 266138032Speter CurChildren = 0; 266238032Speter} 266390792Sgshapiro 266490792Sgshapiro/* 266542575Speter** PROC_LIST_DISPLAY -- display the process list 266642575Speter** 266742575Speter** Parameters: 266842575Speter** out -- output file pointer 266990792Sgshapiro** prefix -- string to output in front of each line. 267042575Speter** 267142575Speter** Returns: 267242575Speter** none. 267342575Speter*/ 267442575Speter 267542575Spetervoid 267690792Sgshapiroproc_list_display(out, prefix) 267790792Sgshapiro SM_FILE_T *out; 267890792Sgshapiro char *prefix; 267942575Speter{ 268042575Speter int i; 268142575Speter 268242575Speter for (i = 0; i < ProcListSize; i++) 268342575Speter { 268442575Speter if (ProcListVec[i].proc_pid == NO_PID) 268542575Speter continue; 268642575Speter 268790792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "%s%d %s%s\n", 268890792Sgshapiro prefix, 268990792Sgshapiro (int) ProcListVec[i].proc_pid, 269090792Sgshapiro ProcListVec[i].proc_task != NULL ? 269190792Sgshapiro ProcListVec[i].proc_task : "(unknown)", 269290792Sgshapiro (OpMode == MD_SMTP || 269390792Sgshapiro OpMode == MD_DAEMON || 269490792Sgshapiro OpMode == MD_ARPAFTP) ? "\r" : ""); 269542575Speter } 269642575Speter} 269790792Sgshapiro 269890792Sgshapiro/* 269990792Sgshapiro** PROC_LIST_SIGNAL -- send a signal to a type of process in the list 270080785Sgshapiro** 270180785Sgshapiro** Parameters: 270290792Sgshapiro** type -- type of process to signal 270390792Sgshapiro** signal -- the type of signal to send 270480785Sgshapiro** 270590792Sgshapiro** Results: 270690792Sgshapiro** none. 270790792Sgshapiro** 270890792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 270990792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 271090792Sgshapiro** DOING. 271180785Sgshapiro*/ 271280785Sgshapiro 271390792Sgshapirovoid 271490792Sgshapiroproc_list_signal(type, signal) 271590792Sgshapiro int type; 271690792Sgshapiro int signal; 271780785Sgshapiro{ 271890792Sgshapiro int chldwasblocked; 271990792Sgshapiro int alrmwasblocked; 272090792Sgshapiro int i; 272190792Sgshapiro pid_t mypid = getpid(); 272280785Sgshapiro 272390792Sgshapiro /* block these signals so that we may signal cleanly */ 272490792Sgshapiro chldwasblocked = sm_blocksignal(SIGCHLD); 272590792Sgshapiro alrmwasblocked = sm_blocksignal(SIGALRM); 272680785Sgshapiro 272790792Sgshapiro /* Find all processes of type and send signal */ 272890792Sgshapiro for (i = 0; i < ProcListSize; i++) 272980785Sgshapiro { 273090792Sgshapiro if (ProcListVec[i].proc_pid == NO_PID || 273190792Sgshapiro ProcListVec[i].proc_pid == mypid) 273290792Sgshapiro continue; 273390792Sgshapiro if (ProcListVec[i].proc_type != type) 273490792Sgshapiro continue; 273590792Sgshapiro (void) kill(ProcListVec[i].proc_pid, signal); 273680785Sgshapiro } 273780785Sgshapiro 273890792Sgshapiro /* restore the signals */ 273990792Sgshapiro if (alrmwasblocked == 0) 274090792Sgshapiro (void) sm_releasesignal(SIGALRM); 274190792Sgshapiro if (chldwasblocked == 0) 274290792Sgshapiro (void) sm_releasesignal(SIGCHLD); 274380785Sgshapiro} 2744