2 * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
5 * Copyright (c) 1988, 1993
6 * The Regents of the University of California. All rights reserved.
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
16 SM_RCSID("@(#)$Id: util.c,v 8.383 2004/08/02 18:50:59 ca Exp $")
22 ** NEWSTR -- Create a copy of a C string
25 ** s -- the string to copy.
28 ** pointer to newly allocated string.
41 sm_strlcpy(n
, s
, l
+ 1);
46 ** ADDQUOTES -- Adds quotes & quote bits to a string.
48 ** Runs through a string and adds backslashes and quote bits.
51 ** s -- the string to modify.
52 ** rpool -- resource pool from which to allocate result
55 ** pointer to quoted string.
70 /* Find length of quoted string */
71 while ((c
= *p
++) != '\0')
74 if (c
== '\\' || c
== '"')
78 q
= r
= sm_rpool_malloc_x(rpool
, len
+ 3);
81 /* add leading quote */
83 while ((c
= *p
++) != '\0')
86 if (c
== '\\' || c
== '"')
96 ** STRIPBACKSLASH -- Strip all leading backslashes from a string, provided
97 ** the following character is alpha-numerical.
99 ** This is done in place.
102 ** s -- the string to strip.
114 if (s
== NULL
|| *s
== '\0')
117 while (*p
== '\\' && (p
[1] == '\\' || (isascii(p
[1]) && isalnum(p
[1]))))
126 ** RFC822_STRING -- Checks string for proper RFC822 string quoting.
128 ** Runs through a string and verifies RFC822 special characters
129 ** are only found inside comments, quoted strings, or backslash
130 ** escaped. Also verified balanced quotes and parenthesis.
133 ** s -- the string to modify.
136 ** true iff the string is RFC822 compliant, false otherwise.
152 /* escaped character */
159 else if (commentlev
== 0 && *c
== '"')
173 else if (commentlev
== 0 &&
174 strchr(MustQuoteChars
, *c
) != NULL
)
180 /* unbalanced '"' or '(' */
181 return !quoted
&& commentlev
== 0;
184 ** SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string
186 ** Arbitrarily shorten (in place) an RFC822 string and rebalance
187 ** comments and quotes.
190 ** string -- the string to shorten
191 ** length -- the maximum size, 0 if no maximum
194 ** true if string is changed, false otherwise
197 ** Changes string in place, possibly resulting
198 ** in a shorter string.
202 shorten_rfc822_string(string
, length
)
206 bool backslash
= false;
207 bool modified
= false;
214 ** If have to rebalance an already short enough string,
215 ** need to do it within allocated space.
218 slen
= strlen(string
);
219 if (length
== 0 || slen
< length
)
232 else if (*ptr
== '(')
237 else if (*ptr
== ')')
239 if (--parencount
< 0)
243 /* Inside a comment, quotes don't matter */
244 if (parencount
<= 0 && *ptr
== '"')
248 /* Check for sufficient space for next character */
249 if (length
- (ptr
- string
) <= (size_t) ((backslash
? 1 : 0) +
253 /* Not enough, backtrack */
256 else if (*ptr
== '(' && !quoted
)
258 else if (*ptr
== '"' && parencount
== 0)
266 while (parencount
-- > 0)
292 ** FIND_CHARACTER -- find an unquoted character in an RFC822 string
294 ** Find an unquoted, non-commented character in an RFC822
295 ** string and return a pointer to its location in the
299 ** string -- the string to search
300 ** character -- the character to find
303 ** pointer to the character, or
304 ** a pointer to the end of the line if character is not found
308 find_character(string
, character
)
312 bool backslash
= false;
316 while (string
!= NULL
&& *string
!= '\0')
321 if (!quoted
&& character
== '\\' && *string
== '\\')
338 if (--parencount
< 0)
343 /* Inside a comment, nothing matters */
352 else if (*string
== character
&& !quoted
)
357 /* Return pointer to the character */
362 ** CHECK_BODYTYPE -- check bodytype parameter
365 ** bodytype -- bodytype parameter
368 ** BODYTYPE_* according to parameter
373 check_bodytype(bodytype
)
376 /* check body type for legality */
377 if (bodytype
== NULL
)
378 return BODYTYPE_NONE
;
379 if (sm_strcasecmp(bodytype
, "7BIT") == 0)
380 return BODYTYPE_7BIT
;
381 if (sm_strcasecmp(bodytype
, "8BITMIME") == 0)
382 return BODYTYPE_8BITMIME
;
383 return BODYTYPE_ILLEGAL
;
386 #if _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI
388 ** TRUNCATE_AT_DELIM -- truncate string at a delimiter and append "..."
391 ** str -- string to truncate
392 ** len -- maximum length (including '\0') (0 for unlimited)
393 ** delim -- delimiter character
400 truncate_at_delim(str
, len
, delim
)
407 if (str
== NULL
|| len
== 0 || strlen(str
) < len
)
410 *(str
+ len
- 1) = '\0';
411 while ((p
= strrchr(str
, delim
)) != NULL
)
414 if (p
- str
+ 4 < len
)
418 (void) sm_strlcat(str
, "...", len
);
423 /* Couldn't find a place to append "..." */
425 (void) sm_strlcpy(str
, "...", len
);
429 #endif /* _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI */
431 ** XALLOC -- Allocate memory, raise an exception on error
434 ** sz -- size of area to allocate.
437 ** pointer to data region.
440 ** SmHeapOutOfMemory (F:sm.heap) -- cannot allocate memory
443 ** Memory is allocated.
448 xalloc_tagged(sz
, file
, line
)
452 #else /* SM_HEAP_CHECK */
455 #endif /* SM_HEAP_CHECK */
459 /* some systems can't handle size zero mallocs */
463 /* scaffolding for testing error handling code */
464 sm_xtrap_raise_x(&SmHeapOutOfMemory
);
466 p
= sm_malloc_tagged((unsigned) sz
, file
, line
, sm_heap_group());
469 sm_exc_raise_x(&SmHeapOutOfMemory
);
474 ** COPYPLIST -- copy list of pointers.
476 ** This routine is the equivalent of strdup for lists of
480 ** list -- list of pointers to copy.
481 ** Must be NULL terminated.
482 ** copycont -- if true, copy the contents of the vector
483 ** (which must be a string) also.
484 ** rpool -- resource pool from which to allocate storage,
492 copyplist(list
, copycont
, rpool
)
498 register char **newvp
;
500 for (vp
= list
; *vp
!= NULL
; vp
++)
505 newvp
= (char **) sm_rpool_malloc_x(rpool
, (vp
- list
) * sizeof *vp
);
506 memmove((char *) newvp
, (char *) list
, (int) (vp
- list
) * sizeof *vp
);
510 for (vp
= newvp
; *vp
!= NULL
; vp
++)
511 *vp
= sm_rpool_strdup_x(rpool
, *vp
);
517 ** COPYQUEUE -- copy address queue.
519 ** This routine is the equivalent of strdup for address queues;
520 ** addresses marked as QS_IS_DEAD() aren't copied
523 ** addr -- list of address structures to copy.
524 ** rpool -- resource pool from which to allocate storage
531 copyqueue(addr
, rpool
)
535 register ADDRESS
*newaddr
;
537 register ADDRESS
**tail
= &ret
;
541 if (!QS_IS_DEAD(addr
->q_state
))
543 newaddr
= (ADDRESS
*) sm_rpool_malloc_x(rpool
,
545 STRUCTCOPY(*addr
, *newaddr
);
547 tail
= &newaddr
->q_next
;
556 ** LOG_SENDMAIL_PID -- record sendmail pid and command line.
559 ** e -- the current envelope.
565 ** writes pidfile, logs command line.
566 ** keeps file open and locked to prevent overwrite of active file
569 static SM_FILE_T
*Pidf
= NULL
;
576 char pidpath
[MAXPATHLEN
];
577 extern char *CommandLineArgs
;
579 /* write the pid to the log file for posterity */
580 sff
= SFF_NOLINK
|SFF_ROOTOK
|SFF_REGONLY
|SFF_CREAT
|SFF_NBLOCK
;
581 if (TrustedUid
!= 0 && RealUid
== TrustedUid
)
582 sff
|= SFF_OPENASROOT
;
583 expand(PidFile
, pidpath
, sizeof pidpath
, e
);
584 Pidf
= safefopen(pidpath
, O_WRONLY
|O_TRUNC
, FileMode
, sff
);
587 if (errno
== EWOULDBLOCK
)
588 sm_syslog(LOG_ERR
, NOQID
,
589 "unable to write pid to %s: file in use by another process",
592 sm_syslog(LOG_ERR
, NOQID
,
593 "unable to write pid to %s: %s",
594 pidpath
, sm_errstring(errno
));
598 PidFilePid
= getpid();
600 /* write the process id on line 1 */
601 (void) sm_io_fprintf(Pidf
, SM_TIME_DEFAULT
, "%ld\n",
604 /* line 2 contains all command line flags */
605 (void) sm_io_fprintf(Pidf
, SM_TIME_DEFAULT
, "%s\n",
609 (void) sm_io_flush(Pidf
, SM_TIME_DEFAULT
);
612 ** Leave pid file open until process ends
613 ** so it's not overwritten by another
618 sm_syslog(LOG_INFO
, NOQID
, "started as: %s", CommandLineArgs
);
622 ** CLOSE_SENDMAIL_PID -- close sendmail pid file
637 (void) sm_io_close(Pidf
, SM_TIME_DEFAULT
);
642 ** SET_DELIVERY_MODE -- set and record the delivery mode
645 ** mode -- delivery mode
646 ** e -- the current envelope.
652 ** sets {deliveryMode} macro
656 set_delivery_mode(mode
, e
)
662 e
->e_sendmode
= (char) mode
;
663 buf
[0] = (char) mode
;
665 macdefine(&e
->e_macro
, A_TEMP
, macid("{deliveryMode}"), buf
);
668 ** SET_OP_MODE -- set and record the op mode
672 ** e -- the current envelope.
678 ** sets {opMode} macro
686 extern ENVELOPE BlankEnvelope
;
688 OpMode
= (char) mode
;
689 buf
[0] = (char) mode
;
691 macdefine(&BlankEnvelope
.e_macro
, A_TEMP
, MID_OPMODE
, buf
);
694 ** PRINTAV -- print argument vector.
697 ** fp -- output file pointer.
698 ** av -- argument vector.
715 sm_dprintf("\n\t%08lx=", (unsigned long) *av
);
717 (void) sm_io_putc(fp
, SM_TIME_DEFAULT
, ' ');
720 (void) sm_io_putc(fp
, SM_TIME_DEFAULT
, '\n');
723 ** XPUTS -- put string doing control escapes.
726 ** fp -- output file pointer.
727 ** s -- string to put.
739 register const char *s
;
742 register struct metamac
*mp
;
743 bool shiftout
= false;
744 extern struct metamac MetaMacros
[];
745 static SM_DEBUG_T DebugANSI
= SM_DEBUG_INITIALIZER("ANSI",
746 "@(#)$Debug: ANSI - enable reverse video in debug output $");
749 ** TermEscape is set here, rather than in main(),
750 ** because ANSI mode can be turned on or off at any time
751 ** if we are in -bt rule testing mode.
754 if (sm_debug_unknown(&DebugANSI
))
756 if (sm_debug_active(&DebugANSI
, 1))
758 TermEscape
.te_rv_on
= "\033[7m";
759 TermEscape
.te_rv_off
= "\033[0m";
763 TermEscape
.te_rv_on
= "";
764 TermEscape
.te_rv_off
= "";
770 (void) sm_io_fprintf(fp
, SM_TIME_DEFAULT
, "%s<null>%s",
771 TermEscape
.te_rv_on
, TermEscape
.te_rv_off
);
774 while ((c
= (*s
++ & 0377)) != '\0')
778 (void) sm_io_fprintf(fp
, SM_TIME_DEFAULT
, "%s",
779 TermEscape
.te_rv_off
);
786 (void) sm_io_fprintf(fp
, SM_TIME_DEFAULT
,
788 TermEscape
.te_rv_on
);
795 if (c
== MACROEXPAND
|| c
== MACRODEXPAND
)
797 (void) sm_io_fprintf(fp
, SM_TIME_DEFAULT
,
799 TermEscape
.te_rv_on
);
800 if (c
== MACRODEXPAND
)
801 (void) sm_io_putc(fp
,
802 SM_TIME_DEFAULT
, '&');
806 if (strchr("=~&?", *s
) != NULL
)
807 (void) sm_io_putc(fp
,
810 if (bitset(0200, *s
))
811 (void) sm_io_fprintf(fp
,
814 macname(bitidx(*s
++)));
816 (void) sm_io_fprintf(fp
,
822 for (mp
= MetaMacros
; mp
->metaname
!= '\0'; mp
++)
824 if (bitidx(mp
->metaval
) == c
)
826 (void) sm_io_fprintf(fp
,
835 if (c
== MATCHCLASS
|| c
== MATCHNCLASS
)
837 if (bitset(0200, *s
))
838 (void) sm_io_fprintf(fp
,
841 macname(bitidx(*s
++)));
843 (void) sm_io_fprintf(fp
,
848 if (mp
->metaname
!= '\0')
851 /* unrecognized meta character */
852 (void) sm_io_fprintf(fp
, SM_TIME_DEFAULT
, "%sM-",
853 TermEscape
.te_rv_on
);
860 (void) sm_io_putc(fp
, SM_TIME_DEFAULT
, c
);
864 /* wasn't a meta-macro -- find another way to print it */
881 (void) sm_io_fprintf(fp
, SM_TIME_DEFAULT
, "%s",
882 TermEscape
.te_rv_on
);
887 (void) sm_io_putc(fp
, SM_TIME_DEFAULT
, '\\');
888 (void) sm_io_putc(fp
, SM_TIME_DEFAULT
, c
);
892 (void) sm_io_putc(fp
, SM_TIME_DEFAULT
, '^');
893 (void) sm_io_putc(fp
, SM_TIME_DEFAULT
, c
^ 0100);
897 (void) sm_io_fprintf(fp
, SM_TIME_DEFAULT
, "%s",
898 TermEscape
.te_rv_off
);
899 (void) sm_io_flush(fp
, SM_TIME_DEFAULT
);
902 ** MAKELOWER -- Translate a line into lower case
905 ** p -- the string to translate. If NULL, return is
912 ** String pointed to by p is translated to lower case.
923 for (; (c
= *p
) != '\0'; p
++)
924 if (isascii(c
) && isupper(c
))
928 ** FIXCRLF -- fix <CR><LF> in line.
930 ** Looks for the <CR><LF> combination and turns it into the
931 ** UNIX canonical <NL> character. It only takes one line,
932 ** i.e., it is assumed that the first <NL> found is the end
936 ** line -- the line to fix.
937 ** stripnl -- if true, strip the newline also.
943 ** line is changed in place.
947 fixcrlf(line
, stripnl
)
953 p
= strchr(line
, '\n');
956 if (p
> line
&& p
[-1] == '\r')
963 ** PUTLINE -- put a line like fputs obeying SMTP conventions
965 ** This routine always guarantees outputing a newline (or CRLF,
966 ** as appropriate) at the end of the string.
970 ** mci -- the mailer connection information.
976 ** output of l to mci->mci_out.
984 putxline(l
, strlen(l
), mci
, PXLF_MAPFROM
);
987 ** PUTXLINE -- putline with flags bits.
989 ** This routine always guarantees outputing a newline (or CRLF,
990 ** as appropriate) at the end of the string.
994 ** len -- the length of the line.
995 ** mci -- the mailer connection information.
996 ** pxflags -- flag bits:
997 ** PXLF_MAPFROM -- map From_ to >From_.
998 ** PXLF_STRIP8BIT -- strip 8th bit.
999 ** PXLF_HEADER -- map bare newline in header to newline space.
1000 ** PXLF_NOADDEOL -- don't add an EOL if one wasn't present.
1006 ** output of l to mci->mci_out.
1010 putxline(l
, len
, mci
, pxflags
)
1017 register char *p
, *end
;
1020 /* strip out 0200 bits -- these can look like TELNET protocol */
1021 if (bitset(MCIF_7BIT
, mci
->mci_flags
) ||
1022 bitset(PXLF_STRIP8BIT
, pxflags
))
1024 register char svchar
;
1026 for (p
= l
; (svchar
= *p
) != '\0'; ++p
)
1027 if (bitset(0200, svchar
))
1028 *p
= svchar
&~ 0200;
1036 /* find the end of the line */
1037 p
= memchr(l
, '\n', end
- l
);
1044 if (TrafficLogFile
!= NULL
)
1045 (void) sm_io_fprintf(TrafficLogFile
, SM_TIME_DEFAULT
,
1046 "%05d >>> ", (int) CurrentPid
);
1048 /* check for line overflow */
1049 while (mci
->mci_mailer
->m_linelimit
> 0 &&
1050 (p
- l
+ slop
) > mci
->mci_mailer
->m_linelimit
)
1053 register char *q
= &l
[mci
->mci_mailer
->m_linelimit
- slop
- 1];
1055 if (l
[0] == '.' && slop
== 0 &&
1056 bitnset(M_XDOT
, mci
->mci_mailer
->m_flags
))
1058 if (sm_io_putc(mci
->mci_out
, SM_TIME_DEFAULT
,
1063 /* record progress for DATA timeout */
1064 DataProgress
= true;
1066 if (TrafficLogFile
!= NULL
)
1067 (void) sm_io_putc(TrafficLogFile
,
1068 SM_TIME_DEFAULT
, '.');
1070 else if (l
[0] == 'F' && slop
== 0 &&
1071 bitset(PXLF_MAPFROM
, pxflags
) &&
1072 strncmp(l
, "From ", 5) == 0 &&
1073 bitnset(M_ESCFROM
, mci
->mci_mailer
->m_flags
))
1075 if (sm_io_putc(mci
->mci_out
, SM_TIME_DEFAULT
,
1080 /* record progress for DATA timeout */
1081 DataProgress
= true;
1083 if (TrafficLogFile
!= NULL
)
1084 (void) sm_io_putc(TrafficLogFile
,
1093 if (sm_io_putc(mci
->mci_out
, SM_TIME_DEFAULT
,
1094 (unsigned char) *l
++) == SM_IO_EOF
)
1101 /* record progress for DATA timeout */
1102 DataProgress
= true;
1108 if (sm_io_putc(mci
->mci_out
, SM_TIME_DEFAULT
, '!') ==
1110 sm_io_fputs(mci
->mci_out
, SM_TIME_DEFAULT
,
1111 mci
->mci_mailer
->m_eol
) ==
1113 sm_io_putc(mci
->mci_out
, SM_TIME_DEFAULT
, ' ') ==
1121 /* record progress for DATA timeout */
1122 DataProgress
= true;
1124 if (TrafficLogFile
!= NULL
)
1126 for (l
= l_base
; l
< q
; l
++)
1127 (void) sm_io_putc(TrafficLogFile
,
1130 (void) sm_io_fprintf(TrafficLogFile
,
1141 /* output last part */
1142 if (l
[0] == '.' && slop
== 0 &&
1143 bitnset(M_XDOT
, mci
->mci_mailer
->m_flags
))
1145 if (sm_io_putc(mci
->mci_out
, SM_TIME_DEFAULT
, '.') ==
1150 /* record progress for DATA timeout */
1151 DataProgress
= true;
1153 if (TrafficLogFile
!= NULL
)
1154 (void) sm_io_putc(TrafficLogFile
,
1155 SM_TIME_DEFAULT
, '.');
1157 else if (l
[0] == 'F' && slop
== 0 &&
1158 bitset(PXLF_MAPFROM
, pxflags
) &&
1159 strncmp(l
, "From ", 5) == 0 &&
1160 bitnset(M_ESCFROM
, mci
->mci_mailer
->m_flags
))
1162 if (sm_io_putc(mci
->mci_out
, SM_TIME_DEFAULT
, '>') ==
1167 /* record progress for DATA timeout */
1168 DataProgress
= true;
1170 if (TrafficLogFile
!= NULL
)
1171 (void) sm_io_putc(TrafficLogFile
,
1172 SM_TIME_DEFAULT
, '>');
1176 if (TrafficLogFile
!= NULL
)
1177 (void) sm_io_putc(TrafficLogFile
,
1180 if (sm_io_putc(mci
->mci_out
, SM_TIME_DEFAULT
,
1181 (unsigned char) *l
) == SM_IO_EOF
)
1188 /* record progress for DATA timeout */
1189 DataProgress
= true;
1195 if (TrafficLogFile
!= NULL
)
1196 (void) sm_io_putc(TrafficLogFile
, SM_TIME_DEFAULT
,
1198 if ((!bitset(PXLF_NOADDEOL
, pxflags
) || !noeol
) &&
1199 sm_io_fputs(mci
->mci_out
, SM_TIME_DEFAULT
,
1200 mci
->mci_mailer
->m_eol
) == SM_IO_EOF
)
1204 /* record progress for DATA timeout */
1205 DataProgress
= true;
1207 if (l
< end
&& *l
== '\n')
1209 if (*++l
!= ' ' && *l
!= '\t' && *l
!= '\0' &&
1210 bitset(PXLF_HEADER
, pxflags
))
1212 if (sm_io_putc(mci
->mci_out
, SM_TIME_DEFAULT
,
1217 /* record progress for DATA timeout */
1218 DataProgress
= true;
1221 if (TrafficLogFile
!= NULL
)
1222 (void) sm_io_putc(TrafficLogFile
,
1223 SM_TIME_DEFAULT
, ' ');
1227 /* record progress for DATA timeout */
1228 DataProgress
= true;
1232 ** XUNLINK -- unlink a file, doing logging as appropriate.
1235 ** f -- name of file to unlink.
1238 ** return value of unlink()
1252 sm_syslog(LOG_DEBUG
, CurEnv
->e_id
, "unlink %s", f
);
1256 if (i
< 0 && LogLevel
> 97)
1257 sm_syslog(LOG_DEBUG
, CurEnv
->e_id
, "%s: unlink-fail %d",
1265 ** SFGETS -- "safe" fgets -- times out and ignores random interrupts.
1268 ** buf -- place to put the input line.
1269 ** siz -- size of buf.
1270 ** fp -- file to read from.
1271 ** timeout -- the timeout before error occurs.
1272 ** during -- what we are trying to read (for error messages).
1275 ** NULL on error (including timeout). This may also leave
1276 ** buf containing a null string.
1282 sfgets(buf
, siz
, fp
, timeout
, during
)
1293 SM_REQUIRE(siz
> 0);
1294 SM_REQUIRE(buf
!= NULL
);
1307 /* convert the timeout to sm_io notation */
1308 io_timeout
= (timeout
<= 0) ? SM_TIME_DEFAULT
: timeout
* 1000;
1309 while (!sm_io_eof(fp
) && !sm_io_error(fp
))
1312 p
= sm_io_fgets(fp
, io_timeout
, buf
, siz
);
1313 if (p
== NULL
&& errno
== EAGAIN
)
1315 /* The sm_io_fgets() call timedout */
1317 sm_syslog(LOG_NOTICE
, CurEnv
->e_id
,
1318 "timeout waiting for input from %.100s during %s",
1325 if (TrafficLogFile
!= NULL
)
1326 (void) sm_io_fprintf(TrafficLogFile
,
1328 "%05d <<< [TIMEOUT]\n",
1333 if (p
!= NULL
|| errno
!= EINTR
)
1335 (void) sm_io_clearerr(fp
);
1339 /* clean up the books and exit */
1344 if (TrafficLogFile
!= NULL
)
1345 (void) sm_io_fprintf(TrafficLogFile
, SM_TIME_DEFAULT
,
1351 if (TrafficLogFile
!= NULL
)
1352 (void) sm_io_fprintf(TrafficLogFile
, SM_TIME_DEFAULT
,
1353 "%05d <<< %s", (int) CurrentPid
, buf
);
1356 for (p
= buf
; *p
!= '\0'; p
++)
1359 else if (!HasEightBits
)
1361 for (p
= buf
; *p
!= '\0'; p
++)
1363 if (bitset(0200, *p
))
1365 HasEightBits
= true;
1373 ** FGETFOLDED -- like fgets, but knows about folded lines.
1376 ** buf -- place to put result.
1377 ** n -- bytes available.
1378 ** f -- file to read from.
1381 ** input line(s) on success, NULL on error or SM_IO_EOF.
1382 ** This will normally be buf -- unless the line is too
1383 ** long, when it will be sm_malloc_x()ed.
1386 ** buf gets lines from f, with continuation lines (lines
1387 ** with leading white space) appended. CRLF's are mapped
1388 ** into single newlines. Any trailing NL is stripped.
1392 fgetfolded(buf
, n
, f
)
1397 register char *p
= buf
;
1402 SM_REQUIRE(buf
!= NULL
);
1411 while ((i
= sm_io_getc(f
, SM_TIME_DEFAULT
)) != SM_IO_EOF
)
1415 i
= sm_io_getc(f
, SM_TIME_DEFAULT
);
1419 (void) sm_io_ungetc(f
, SM_TIME_DEFAULT
,
1426 /* allocate new space */
1431 if (nn
< MEMCHUNKSIZE
)
1435 nbp
= sm_malloc_x(nn
);
1436 memmove(nbp
, bp
, p
- bp
);
1447 i
= sm_io_getc(f
, SM_TIME_DEFAULT
);
1449 (void) sm_io_ungetc(f
, SM_TIME_DEFAULT
, i
);
1450 if (i
!= ' ' && i
!= '\t')
1462 ** CURTIME -- return current time.
1468 ** the current time.
1480 ** ATOBOOL -- convert a string representation to boolean.
1482 ** Defaults to false
1485 ** s -- string to convert. Takes "tTyY", empty, and NULL as true,
1489 ** A boolean representation of the string.
1496 if (s
== NULL
|| *s
== '\0' || strchr("tTyY", *s
) != NULL
)
1501 ** ATOOCT -- convert a string representation to octal.
1504 ** s -- string to convert.
1507 ** An integer representing the string interpreted as an
1517 while (*s
>= '0' && *s
<= '7')
1518 i
= (i
<< 3) | (*s
++ - '0');
1522 ** BITINTERSECT -- tell if two bitmaps intersect
1525 ** a, b -- the bitmaps in question
1528 ** true if they have a non-null intersection
1539 for (i
= BITMAPBYTES
/ sizeof (int); --i
>= 0; )
1541 if ((a
[i
] & b
[i
]) != 0)
1547 ** BITZEROP -- tell if a bitmap is all zero
1550 ** map -- the bit map to check
1553 ** true if map is all zero.
1554 ** false if there are any bits set in map.
1563 for (i
= BITMAPBYTES
/ sizeof (int); --i
>= 0; )
1571 ** STRCONTAINEDIN -- tell if one string is contained in another
1574 ** icase -- ignore case?
1575 ** a -- possible substring.
1576 ** b -- possible superstring.
1579 ** true if a is contained in b (case insensitive).
1584 strcontainedin(icase
, a
, b
)
1596 if (icase
&& isascii(c
) && isupper(c
))
1598 for (; lb
-- >= la
; b
++)
1603 isascii(*b
) && isupper(*b
) && tolower(*b
) != c
)
1605 if (sm_strncasecmp(a
, b
, la
) == 0)
1612 if (strncmp(a
, b
, la
) == 0)
1619 ** CHECKFD012 -- check low numbered file descriptors
1621 ** File descriptors 0, 1, and 2 should be open at all times.
1622 ** This routine verifies that, and fixes it if not true.
1625 ** where -- a tag printed if the assertion failed
1638 for (i
= 0; i
< 3; i
++)
1643 ** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging
1646 ** fd -- file descriptor to check.
1647 ** where -- tag to print on failure.
1654 checkfdopen(fd
, where
)
1661 if (fstat(fd
, &st
) < 0 && errno
== EBADF
)
1663 syserr("checkfdopen(%d): %s not open as expected!", fd
, where
);
1669 ** CHECKFDS -- check for new or missing file descriptors
1672 ** where -- tag for printing. If null, take a base line.
1678 ** If where is set, shows changes since the last call.
1687 bool printhdr
= true;
1688 int save_errno
= errno
;
1689 static BITMAP256 baseline
;
1690 extern int DtableSize
;
1692 if (DtableSize
> BITMAPBITS
)
1697 clrbitmap(baseline
);
1699 for (fd
= 0; fd
< maxfd
; fd
++)
1703 if (fstat(fd
, &stbuf
) < 0 && errno
!= EOPNOTSUPP
)
1705 if (!bitnset(fd
, baseline
))
1707 clrbitn(fd
, baseline
);
1709 else if (!bitnset(fd
, baseline
))
1710 setbitn(fd
, baseline
);
1714 /* file state has changed */
1719 sm_syslog(LOG_DEBUG
, CurEnv
->e_id
,
1724 dumpfd(fd
, true, true);
1729 ** PRINTOPENFDS -- print the open file descriptors (for debugging)
1732 ** logit -- if set, send output to syslog; otherwise
1733 ** print for debugging.
1739 #if NETINET || NETINET6
1740 # include <arpa/inet.h>
1741 #endif /* NETINET || NETINET6 */
1748 extern int DtableSize
;
1750 for (fd
= 0; fd
< DtableSize
; fd
++)
1751 dumpfd(fd
, false, logit
);
1754 ** DUMPFD -- dump a file descriptor
1757 ** fd -- the file descriptor to dump.
1758 ** printclosed -- if set, print a notification even if
1759 ** it is closed; otherwise print nothing.
1760 ** logit -- if set, use sm_syslog instead of sm_dprintf()
1767 dumpfd(fd
, printclosed
, logit
)
1776 #endif /* S_IFSOCK */
1777 auto SOCKADDR_LEN_T slen
;
1781 #else /* STAT64 > 0 */
1783 #endif /* STAT64 > 0 */
1787 (void) sm_snprintf(p
, SPACELEFT(buf
, p
), "%3d: ", fd
);
1793 #else /* STAT64 > 0 */
1795 #endif /* STAT64 > 0 */
1800 (void) sm_snprintf(p
, SPACELEFT(buf
, p
),
1802 sm_errstring(errno
));
1805 else if (printclosed
)
1807 (void) sm_snprintf(p
, SPACELEFT(buf
, p
), "CLOSED");
1813 i
= fcntl(fd
, F_GETFL
, 0);
1816 (void) sm_snprintf(p
, SPACELEFT(buf
, p
), "fl=0x%x, ", i
);
1820 (void) sm_snprintf(p
, SPACELEFT(buf
, p
), "mode=%o: ",
1823 switch (st
.st_mode
& S_IFMT
)
1827 (void) sm_snprintf(p
, SPACELEFT(buf
, p
), "SOCK ");
1829 memset(&sa
, '\0', sizeof sa
);
1831 if (getsockname(fd
, &sa
.sa
, &slen
) < 0)
1832 (void) sm_snprintf(p
, SPACELEFT(buf
, p
), "(%s)",
1833 sm_errstring(errno
));
1836 hp
= hostnamebyanyaddr(&sa
);
1843 else if (sa
.sa
.sa_family
== AF_INET
)
1844 (void) sm_snprintf(p
, SPACELEFT(buf
, p
),
1845 "%s/%d", hp
, ntohs(sa
.sin
.sin_port
));
1846 # endif /* NETINET */
1848 else if (sa
.sa
.sa_family
== AF_INET6
)
1849 (void) sm_snprintf(p
, SPACELEFT(buf
, p
),
1850 "%s/%d", hp
, ntohs(sa
.sin6
.sin6_port
));
1851 # endif /* NETINET6 */
1853 (void) sm_snprintf(p
, SPACELEFT(buf
, p
),
1857 (void) sm_snprintf(p
, SPACELEFT(buf
, p
), "->");
1860 if (getpeername(fd
, &sa
.sa
, &slen
) < 0)
1861 (void) sm_snprintf(p
, SPACELEFT(buf
, p
), "(%s)",
1862 sm_errstring(errno
));
1865 hp
= hostnamebyanyaddr(&sa
);
1872 else if (sa
.sa
.sa_family
== AF_INET
)
1873 (void) sm_snprintf(p
, SPACELEFT(buf
, p
),
1874 "%s/%d", hp
, ntohs(sa
.sin
.sin_port
));
1875 # endif /* NETINET */
1877 else if (sa
.sa
.sa_family
== AF_INET6
)
1878 (void) sm_snprintf(p
, SPACELEFT(buf
, p
),
1879 "%s/%d", hp
, ntohs(sa
.sin6
.sin6_port
));
1880 # endif /* NETINET6 */
1882 (void) sm_snprintf(p
, SPACELEFT(buf
, p
),
1886 #endif /* S_IFSOCK */
1889 (void) sm_snprintf(p
, SPACELEFT(buf
, p
), "CHR: ");
1895 (void) sm_snprintf(p
, SPACELEFT(buf
, p
), "BLK: ");
1898 #endif /* S_IFBLK */
1900 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
1902 (void) sm_snprintf(p
, SPACELEFT(buf
, p
), "FIFO: ");
1905 #endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */
1909 (void) sm_snprintf(p
, SPACELEFT(buf
, p
), "DIR: ");
1912 #endif /* S_IFDIR */
1916 (void) sm_snprintf(p
, SPACELEFT(buf
, p
), "LNK: ");
1919 #endif /* S_IFLNK */
1923 (void) sm_snprintf(p
, SPACELEFT(buf
, p
),
1924 "dev=%d/%d, ino=%llu, nlink=%d, u/gid=%d/%d, ",
1925 major(st
.st_dev
), minor(st
.st_dev
),
1926 (ULONGLONG_T
) st
.st_ino
,
1927 (int) st
.st_nlink
, (int) st
.st_uid
,
1930 (void) sm_snprintf(p
, SPACELEFT(buf
, p
), "size=%llu",
1931 (ULONGLONG_T
) st
.st_size
);
1937 sm_syslog(LOG_DEBUG
, CurEnv
? CurEnv
->e_id
: NULL
,
1940 sm_dprintf("%s\n", buf
);
1943 ** SHORTEN_HOSTNAME -- strip local domain information off of hostname.
1946 ** host -- the host to shorten (stripped in place).
1949 ** place where string was truncated, NULL if not truncated.
1953 shorten_hostname(host
)
1961 /* strip off final dot */
1963 p
= &host
[(i
== 0) ? 0 : i
- 1];
1970 /* see if there is any domain at all -- if not, we are done */
1971 p
= strchr(host
, '.');
1975 /* yes, we have a domain -- see if it looks like us */
1976 mydom
= macvalue('m', CurEnv
);
1980 if ((canon
? sm_strcasecmp(p
, mydom
)
1981 : sm_strncasecmp(p
, mydom
, i
)) == 0 &&
1982 (mydom
[i
] == '.' || mydom
[i
] == '\0'))
1990 ** PROG_OPEN -- open a program for reading
1993 ** argv -- the argument list.
1994 ** pfd -- pointer to a place to store the file descriptor.
1995 ** e -- the current envelope.
1998 ** pid of the process -- -1 if it failed.
2002 prog_open(argv
, pfd
, e
)
2013 char buf
[MAXPATHLEN
];
2014 extern int DtableSize
;
2018 syserr("%s: cannot create pipe for stdout", argv
[0]);
2024 syserr("%s: cannot fork", argv
[0]);
2025 (void) close(fdv
[0]);
2026 (void) close(fdv
[1]);
2032 (void) close(fdv
[1]);
2037 /* Reset global flags */
2038 RestartRequest
= NULL
;
2039 RestartWorkGroup
= false;
2040 ShutdownRequest
= NULL
;
2042 CurrentPid
= getpid();
2045 ** Initialize exception stack and default exception
2046 ** handler for child process.
2049 sm_exc_newthread(fatal_error
);
2051 /* child -- close stdin */
2054 /* stdout goes back to parent */
2055 (void) close(fdv
[0]);
2056 if (dup2(fdv
[1], 1) < 0)
2058 syserr("%s: cannot dup2 for stdout", argv
[0]);
2061 (void) close(fdv
[1]);
2063 /* stderr goes to transcript if available */
2064 if (e
->e_xfp
!= NULL
)
2068 xfd
= sm_io_getinfo(e
->e_xfp
, SM_IO_WHAT_FD
, NULL
);
2069 if (xfd
>= 0 && dup2(xfd
, 2) < 0)
2071 syserr("%s: cannot dup2 for stderr", argv
[0]);
2076 /* this process has no right to the queue file */
2077 if (e
->e_lockfp
!= NULL
)
2078 (void) close(sm_io_getinfo(e
->e_lockfp
, SM_IO_WHAT_FD
, NULL
));
2080 /* chroot to the program mailer directory, if defined */
2081 if (ProgMailer
!= NULL
&& ProgMailer
->m_rootdir
!= NULL
)
2083 expand(ProgMailer
->m_rootdir
, buf
, sizeof buf
, e
);
2084 if (chroot(buf
) < 0)
2086 syserr("prog_open: cannot chroot(%s)", buf
);
2091 syserr("prog_open: cannot chdir(/)");
2096 /* run as default user */
2098 sm_mbdb_terminate();
2099 if (setgid(DefGid
) < 0 && geteuid() == 0)
2101 syserr("prog_open: setgid(%ld) failed", (long) DefGid
);
2104 if (setuid(DefUid
) < 0 && geteuid() == 0)
2106 syserr("prog_open: setuid(%ld) failed", (long) DefUid
);
2110 /* run in some directory */
2111 if (ProgMailer
!= NULL
)
2112 p
= ProgMailer
->m_execdir
;
2115 for (; p
!= NULL
; p
= q
)
2120 expand(p
, buf
, sizeof buf
, e
);
2123 if (buf
[0] != '\0' && chdir(buf
) >= 0)
2128 /* backup directories */
2129 if (chdir("/tmp") < 0)
2133 /* Check safety of program to be run */
2134 sff
= SFF_ROOTOK
|SFF_EXECOK
;
2135 if (!bitnset(DBS_RUNWRITABLEPROGRAM
, DontBlameSendmail
))
2136 sff
|= SFF_NOGWFILES
|SFF_NOWWFILES
;
2137 if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH
, DontBlameSendmail
))
2138 sff
|= SFF_NOPATHCHECK
;
2140 sff
|= SFF_SAFEDIRPATH
;
2141 ret
= safefile(argv
[0], DefUid
, DefGid
, DefUser
, sff
, 0, NULL
);
2143 sm_syslog(LOG_INFO
, e
->e_id
,
2144 "Warning: prog_open: program %s unsafe: %s",
2145 argv
[0], sm_errstring(ret
));
2147 /* arrange for all the files to be closed */
2148 sm_close_on_exec(STDERR_FILENO
+ 1, DtableSize
);
2150 /* now exec the process */
2151 (void) execve(argv
[0], (ARGV_T
) argv
, (ARGV_T
) UserEnviron
);
2155 syserr("%s: cannot exec", argv
[0]);
2156 if (transienterror(save_errno
))
2159 return -1; /* avoid compiler warning on IRIX */
2162 ** GET_COLUMN -- look up a Column in a line buffer
2165 ** line -- the raw text line to search.
2166 ** col -- the column number to fetch.
2167 ** delim -- the delimiter between columns. If null,
2169 ** buf -- the output buffer.
2170 ** buflen -- the length of buf.
2173 ** buf if successful.
2178 get_column(line
, col
, delim
, buf
, buflen
)
2190 if ((char) delim
== '\0')
2191 (void) sm_strlcpy(delimbuf
, "\n\t ", sizeof delimbuf
);
2194 delimbuf
[0] = (char) delim
;
2200 return NULL
; /* line empty */
2201 if (*p
== (char) delim
&& col
== 0)
2202 return NULL
; /* first column empty */
2206 if (col
== 0 && (char) delim
== '\0')
2208 while (*begin
!= '\0' && isascii(*begin
) && isspace(*begin
))
2212 for (i
= 0; i
< col
; i
++)
2214 if ((begin
= strpbrk(begin
, delimbuf
)) == NULL
)
2215 return NULL
; /* no such column */
2217 if ((char) delim
== '\0')
2219 while (*begin
!= '\0' && isascii(*begin
) && isspace(*begin
))
2224 end
= strpbrk(begin
, delimbuf
);
2231 (void) sm_strlcpy(buf
, begin
, i
+ 1);
2235 ** CLEANSTRCPY -- copy string keeping out bogus characters
2238 ** t -- "to" string.
2239 ** f -- "from" string.
2240 ** l -- length of space available in "to" string.
2247 cleanstrcpy(t
, f
, l
)
2252 /* check for newlines and log if necessary */
2253 (void) denlstring(f
, true, true);
2256 syserr("!cleanstrcpy: length == 0");
2259 while (l
> 0 && *f
!= '\0')
2262 (isalnum(*f
) || strchr("!#$%&'*+-./^_`{|}~", *f
) != NULL
))
2272 ** DENLSTRING -- convert newlines in a string to spaces
2275 ** s -- the input string
2276 ** strict -- if set, don't permit continuation lines.
2277 ** logattacks -- if set, log attempted attacks.
2280 ** A pointer to a version of the string with newlines
2281 ** mapped to spaces. This should be copied.
2285 denlstring(s
, strict
, logattacks
)
2292 static char *bp
= NULL
;
2296 while ((p
= strchr(p
, '\n')) != NULL
)
2297 if (strict
|| (*++p
!= ' ' && *p
!= '\t'))
2305 /* allocate more space */
2306 char *nbp
= sm_pmalloc_x(l
);
2313 (void) sm_strlcpy(bp
, s
, l
);
2314 for (p
= bp
; (p
= strchr(p
, '\n')) != NULL
; )
2319 sm_syslog(LOG_NOTICE
, CurEnv
->e_id
,
2320 "POSSIBLE ATTACK from %.100s: newline in string \"%s\"",
2321 RealHostName
== NULL
? "[UNKNOWN]" : RealHostName
,
2322 shortenstring(bp
, MAXSHORTSTR
));
2329 ** STRREPLNONPRT -- replace "unprintable" characters in a string with subst
2332 ** s -- string to manipulate (in place)
2333 ** subst -- character to use as replacement
2336 ** true iff string did not contain "unprintable" characters
2351 if (!(isascii(*s
) && isprint(*s
)))
2362 ** STR2PRT -- convert "unprintable" characters in a string to \oct
2365 ** s -- string to convert
2368 ** converted string.
2369 ** This is a static local buffer, string must be copied
2370 ** before this function is called again!
2381 static char *buf
= NULL
;
2386 for (h
= s
, l
= 1; *h
!= '\0'; h
++, l
++)
2393 else if (!(isascii(*h
) && isprint(*h
)))
2403 char *nbuf
= sm_pmalloc_x(l
);
2410 for (h
= buf
; *s
!= '\0' && l
> 0; s
++, l
--)
2413 if (isascii(c
) && isprint(c
) && c
!= '\\')
2436 (void) sm_snprintf(h
, l
, "%03o",
2437 (unsigned int)((unsigned char) c
));
2440 ** XXX since l is unsigned this may
2441 ** wrap around if the calculation is screwed
2452 buf
[len
- 1] = '\0';
2456 ** PATH_IS_DIR -- check to see if file exists and is a directory.
2458 ** There are some additional checks for security violations in
2459 ** here. This routine is intended to be used for the host status
2463 ** pathname -- pathname to check for directory-ness.
2464 ** createflag -- if set, create directory if needed.
2467 ** true -- if the indicated pathname is a directory
2468 ** false -- otherwise
2472 path_is_dir(pathname
, createflag
)
2476 struct stat statbuf
;
2479 if (lstat(pathname
, &statbuf
) < 0)
2480 #else /* HASLSTAT */
2481 if (stat(pathname
, &statbuf
) < 0)
2482 #endif /* HASLSTAT */
2484 if (errno
!= ENOENT
|| !createflag
)
2486 if (mkdir(pathname
, 0755) < 0)
2490 if (!S_ISDIR(statbuf
.st_mode
))
2496 /* security: don't allow writable directories */
2497 if (bitset(S_IWGRP
|S_IWOTH
, statbuf
.st_mode
))
2505 ** PROC_LIST_ADD -- add process id to list of our children
2508 ** pid -- pid to add to list.
2509 ** task -- task of pid.
2510 ** type -- type of process.
2511 ** count -- number of processes.
2512 ** other -- other information for this type.
2518 ** May increase CurChildren. May grow ProcList.
2521 typedef struct procs PROCS_T
;
2530 SOCKADDR proc_hostaddr
;
2533 static PROCS_T
*volatile ProcListVec
= NULL
;
2534 static int ProcListSize
= 0;
2537 proc_list_add(pid
, task
, type
, count
, other
, hostaddr
)
2547 for (i
= 0; i
< ProcListSize
; i
++)
2549 if (ProcListVec
[i
].proc_pid
== NO_PID
)
2552 if (i
>= ProcListSize
)
2554 /* probe the existing vector to avoid growing infinitely */
2557 /* now scan again */
2558 for (i
= 0; i
< ProcListSize
; i
++)
2560 if (ProcListVec
[i
].proc_pid
== NO_PID
)
2564 if (i
>= ProcListSize
)
2566 /* grow process list */
2569 SM_ASSERT(ProcListSize
< INT_MAX
- PROC_LIST_SEG
);
2570 npv
= (PROCS_T
*) sm_pmalloc_x((sizeof *npv
) *
2571 (ProcListSize
+ PROC_LIST_SEG
));
2572 if (ProcListSize
> 0)
2574 memmove(npv
, ProcListVec
,
2575 ProcListSize
* sizeof (PROCS_T
));
2576 sm_free(ProcListVec
);
2579 /* XXX just use memset() to initialize this part? */
2580 for (i
= ProcListSize
; i
< ProcListSize
+ PROC_LIST_SEG
; i
++)
2582 npv
[i
].proc_pid
= NO_PID
;
2583 npv
[i
].proc_task
= NULL
;
2584 npv
[i
].proc_type
= PROC_NONE
;
2587 ProcListSize
+= PROC_LIST_SEG
;
2590 ProcListVec
[i
].proc_pid
= pid
;
2591 PSTRSET(ProcListVec
[i
].proc_task
, task
);
2592 ProcListVec
[i
].proc_type
= type
;
2593 ProcListVec
[i
].proc_count
= count
;
2594 ProcListVec
[i
].proc_other
= other
;
2595 if (hostaddr
!= NULL
)
2596 ProcListVec
[i
].proc_hostaddr
= *hostaddr
;
2598 memset(&ProcListVec
[i
].proc_hostaddr
, 0,
2599 sizeof(ProcListVec
[i
].proc_hostaddr
));
2601 /* if process adding itself, it's not a child */
2602 if (pid
!= CurrentPid
)
2604 SM_ASSERT(CurChildren
< INT_MAX
);
2609 ** PROC_LIST_SET -- set pid task in process list
2612 ** pid -- pid to set
2613 ** task -- task of pid
2620 proc_list_set(pid
, task
)
2626 for (i
= 0; i
< ProcListSize
; i
++)
2628 if (ProcListVec
[i
].proc_pid
== pid
)
2630 PSTRSET(ProcListVec
[i
].proc_task
, task
);
2636 ** PROC_LIST_DROP -- drop pid from process list
2639 ** pid -- pid to drop
2640 ** st -- process status
2641 ** other -- storage for proc_other (return).
2647 ** May decrease CurChildren, CurRunners, or
2648 ** set RestartRequest or ShutdownRequest.
2650 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
2651 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2656 proc_list_drop(pid
, st
, other
)
2662 int type
= PROC_NONE
;
2664 for (i
= 0; i
< ProcListSize
; i
++)
2666 if (ProcListVec
[i
].proc_pid
== pid
)
2668 ProcListVec
[i
].proc_pid
= NO_PID
;
2669 type
= ProcListVec
[i
].proc_type
;
2671 *other
= ProcListVec
[i
].proc_other
;
2675 if (CurChildren
> 0)
2679 if (type
== PROC_CONTROL
&& WIFEXITED(st
))
2681 /* if so, see if we need to restart or shutdown */
2682 if (WEXITSTATUS(st
) == EX_RESTART
)
2683 RestartRequest
= "control socket";
2684 else if (WEXITSTATUS(st
) == EX_SHUTDOWN
)
2685 ShutdownRequest
= "control socket";
2687 else if (type
== PROC_QUEUE_CHILD
&& !WIFSTOPPED(st
) &&
2688 ProcListVec
[i
].proc_other
> -1)
2690 /* restart this persistent runner */
2691 mark_work_group_restart(ProcListVec
[i
].proc_other
, st
);
2693 else if (type
== PROC_QUEUE
)
2694 CurRunners
-= ProcListVec
[i
].proc_count
;
2697 ** PROC_LIST_CLEAR -- clear the process list
2706 ** Sets CurChildren to zero.
2714 /* start from 1 since 0 is the daemon itself */
2715 for (i
= 1; i
< ProcListSize
; i
++)
2716 ProcListVec
[i
].proc_pid
= NO_PID
;
2720 ** PROC_LIST_PROBE -- probe processes in the list to see if they still exist
2729 ** May decrease CurChildren.
2737 /* start from 1 since 0 is the daemon itself */
2738 for (i
= 1; i
< ProcListSize
; i
++)
2740 if (ProcListVec
[i
].proc_pid
== NO_PID
)
2742 if (kill(ProcListVec
[i
].proc_pid
, 0) < 0)
2745 sm_syslog(LOG_DEBUG
, CurEnv
->e_id
,
2746 "proc_list_probe: lost pid %d",
2747 (int) ProcListVec
[i
].proc_pid
);
2748 ProcListVec
[i
].proc_pid
= NO_PID
;
2749 SM_FREE_CLR(ProcListVec
[i
].proc_task
);
2753 if (CurChildren
< 0)
2758 ** PROC_LIST_DISPLAY -- display the process list
2761 ** out -- output file pointer
2762 ** prefix -- string to output in front of each line.
2769 proc_list_display(out
, prefix
)
2775 for (i
= 0; i
< ProcListSize
; i
++)
2777 if (ProcListVec
[i
].proc_pid
== NO_PID
)
2780 (void) sm_io_fprintf(out
, SM_TIME_DEFAULT
, "%s%d %s%s\n",
2782 (int) ProcListVec
[i
].proc_pid
,
2783 ProcListVec
[i
].proc_task
!= NULL
?
2784 ProcListVec
[i
].proc_task
: "(unknown)",
2785 (OpMode
== MD_SMTP
||
2786 OpMode
== MD_DAEMON
||
2787 OpMode
== MD_ARPAFTP
) ? "\r" : "");
2792 ** PROC_LIST_SIGNAL -- send a signal to a type of process in the list
2795 ** type -- type of process to signal
2796 ** signal -- the type of signal to send
2801 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
2802 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2807 proc_list_signal(type
, signal
)
2814 pid_t mypid
= getpid();
2816 /* block these signals so that we may signal cleanly */
2817 chldwasblocked
= sm_blocksignal(SIGCHLD
);
2818 alrmwasblocked
= sm_blocksignal(SIGALRM
);
2820 /* Find all processes of type and send signal */
2821 for (i
= 0; i
< ProcListSize
; i
++)
2823 if (ProcListVec
[i
].proc_pid
== NO_PID
||
2824 ProcListVec
[i
].proc_pid
== mypid
)
2826 if (ProcListVec
[i
].proc_type
!= type
)
2828 (void) kill(ProcListVec
[i
].proc_pid
, signal
);
2831 /* restore the signals */
2832 if (alrmwasblocked
== 0)
2833 (void) sm_releasesignal(SIGALRM
);
2834 if (chldwasblocked
== 0)
2835 (void) sm_releasesignal(SIGCHLD
);
2839 ** COUNT_OPEN_CONNECTIONS
2842 ** hostaddr - ClientAddress
2845 ** the number of open connections for this client
2850 count_open_connections(hostaddr
)
2855 if (hostaddr
== NULL
)
2858 for (i
= 0; i
< ProcListSize
; i
++)
2860 if (ProcListVec
[i
].proc_pid
== NO_PID
)
2863 if (hostaddr
->sa
.sa_family
!=
2864 ProcListVec
[i
].proc_hostaddr
.sa
.sa_family
)
2867 if (hostaddr
->sa
.sa_family
== AF_INET
&&
2868 (hostaddr
->sin
.sin_addr
.s_addr
==
2869 ProcListVec
[i
].proc_hostaddr
.sin
.sin_addr
.s_addr
))
2871 #endif /* NETINET */
2873 if (hostaddr
->sa
.sa_family
== AF_INET6
&&
2874 IN6_ARE_ADDR_EQUAL(&(hostaddr
->sin6
.sin6_addr
),
2875 &(ProcListVec
[i
].proc_hostaddr
.sin6
.sin6_addr
)))
2877 #endif /* NETINET6 */