sendmail runs out of memory on long lists
Steven M. Schultz
sms at wlv.imsd.contel.com
Sat Nov 25 09:24:17 AEST 1989
Subject: sendmail and long recipient lists
Index: usr.lib/sendmail/src/Makefile.m4 2.10BSD
Description:
Even with string extraction being used (using the sendmail
preprocessor 'mkstrcc') 'sendmail' runs out of memory when
processing very long lists of recipients.
Repeat-By:
Attempt to send a mail item which has 120+ people in the To:
line.
Fix:
No changes to the 'sendmail' source are required, instead
two new source files are added and the Makefile.m4 file
is patched. If you do not have the latest Makefile.m4 and
'mkstrcc' shell script send a mail item to "sms at wlv.imsd.contel.com"
and i will mail them to you.
Aside from 'sendmail's voracious appetite for memory, compounded
by malloc()'s apparently not always being matched by free()'s,
one of the largest single contributors to the D space of 'sendmail'
is the ctime(3) (and associated routines localtime(3), etc)
function. The tables utilized for the Daylight Savings Time automatic
timezone adjustment are substantial. The solution implemented
by the modules below move the ctime(3) logic to a separate
process (/usr/lib/ctimed) and use a local ctime.o in 'sendmail' to
communicate via a bidirectional pipe to the external process.
This SAVES 2.5KB of D space! The cost is low, one process table
table entry occupied permanently, and a small ('sendmail' does not
call the ctime(3) routines more than twice or thrice per invocation
based on the debugging of 'ctimed') amount of overhead for each mail
item.
Due to the peculiar forking sequence 'sendmail' performs upon
startup, the 'ctimed' pid for the "sendmail -bd" process will
be one lower than that of the daemon 'sendmail'.
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
# patch
# ctime.c
# ctimed.c
# This archive created: Fri Nov 24 16:53:28 1989
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'patch'
then
echo shar: "will not over-write existing file 'patch'"
else
sed 's/^X//' << \SHAR_EOF > 'patch'
X*** Makefile.m4.ol Mon Feb 6 12:02:24 1989
X--- Makefile.m4 Wed Nov 1 16:12:05 1989
X***************
X*** 26,38 ****
X savemail.o err.o readcf.o stab.o headers.o recipient.o \
X stats.o daemon.o usersmtp.o srvrsmtp.o queue.o \
X macro.o util.o clock.o trace.o envelope.o
X! OBJS2= sysexits.o arpadate.o convtime.o
X OBJS= $(OBJS1) $(OBJS2) $(EXTRACT)
X
X SBASE= conf.o collect.o parseaddr.o alias.o deliver.o stab.o headers.o \
X recipient.o stats.o srvrsmtp.o queue.o macro.o util.o clock.o \
X trace.o envelope.o sysexits.o arpadate.o convtime.o Version.o \
X! $(EXTRACT)
X SOV1= main.o readcf.o
X SOV2= daemon.o savemail.o usersmtp.o err.o
X
X--- 26,38 ----
X savemail.o err.o readcf.o stab.o headers.o recipient.o \
X stats.o daemon.o usersmtp.o srvrsmtp.o queue.o \
X macro.o util.o clock.o trace.o envelope.o
X! OBJS2= sysexits.o arpadate.o convtime.o ctime.o
X OBJS= $(OBJS1) $(OBJS2) $(EXTRACT)
X
X SBASE= conf.o collect.o parseaddr.o alias.o deliver.o stab.o headers.o \
X recipient.o stats.o srvrsmtp.o queue.o macro.o util.o clock.o \
X trace.o envelope.o sysexits.o arpadate.o convtime.o Version.o \
X! ctime.o $(EXTRACT)
X SOV1= main.o readcf.o
X SOV2= daemon.o savemail.o usersmtp.o err.o
X
X***************
X*** 41,49 ****
X sysexits.c util.c arpadate.c version.c collect.c \
X macro.c headers.c readcf.c stab.c recipient.c stats.c daemon.c \
X usersmtp.c srvrsmtp.c queue.c clock.c trace.c envelope.c
X! SRCS2= TODO convtime.c
X SRCS= Version.c $(SRCS1) $(SRCS2)
X! ALL= sendmail
X
X CHOWN= -echo chown
X CHMOD= chmod
X--- 41,49 ----
X sysexits.c util.c arpadate.c version.c collect.c \
X macro.c headers.c readcf.c stab.c recipient.c stats.c daemon.c \
X usersmtp.c srvrsmtp.c queue.c clock.c trace.c envelope.c
X! SRCS2= TODO convtime.c ctime.c
X SRCS= Version.c $(SRCS1) $(SRCS2)
X! ALL= sendmail ctimed
X
X CHOWN= -echo chown
X CHMOD= chmod
X***************
X*** 85,90 ****
X--- 85,93 ----
X $(CHMOD) $(OBJMODE) sendmail
X size sendmail; ls -l sendmail; ifdef(`m4SCCS', `$(WHAT) < Version.o')
X
X+ ctimed:
X+ cc $(SEPFLAG) $(CFLAGS) ctimed.c -o ctimed
X+
X install: all
X $(INSTALL) -m 4755 sendmail $(DESTDIR)/usr/lib
X chgrp kmem $(DESTDIR)/usr/lib/sendmail
X***************
X*** 93,98 ****
X--- 96,102 ----
X install -c -o bin -m 644 sendmail.sr \
X $(DESTDIR)/usr/lib/sendmail.sr; \
X fi
X+ install -c -s -o bin -m 0755 ctimed /usr/lib/ctimed
X
X version: newversion $(OBJS) Version.c
X
X***************
X*** 137,143 ****
X
X clean:
X rm -f core sendmail rmail usersmtp uucp a.out XREF sendmail.cf
X! rm -f sendmail.sr *.o
X
X sources: $(SRCS)
X
X--- 141,147 ----
X
X clean:
X rm -f core sendmail rmail usersmtp uucp a.out XREF sendmail.cf
X! rm -f sendmail.sr *.o ctimed
X
X sources: $(SRCS)
SHAR_EOF
fi
if test -f 'ctime.c'
then
echo shar: "will not over-write existing file 'ctime.c'"
else
sed 's/^X//' << \SHAR_EOF > 'ctime.c'
X#include <stdio.h>
X#include <sysexits.h>
X#include <sys/types.h>
X#include <sys/time.h>
X
X#define SEND_FD W[1]
X#define RECV_FD R[0]
X
X#define CTIME 1
X#define ASCTIME 2
X#define TZSET 3
X#define LOCALTIME 4
X#define GMTIME 5
X#define OFFTIME 6
X
X static int R[2], W[2], inited;
X static char result[26];
X static struct tm tmtmp;
X
Xchar *
Xctime(t)
X time_t *t;
X {
X u_char fnc = CTIME;
X
X sewer();
X write(SEND_FD, &fnc, sizeof fnc);
X write(SEND_FD, t, sizeof (*t));
X getb(RECV_FD, result, 26);
X return(result);
X }
X
Xchar *
Xasctime(tp)
X struct tm *tp;
X {
X u_char fnc = ASCTIME;
X
X sewer();
X write(SEND_FD, &fnc, sizeof fnc);
X write(SEND_FD, tp, sizeof (*tp));
X getb(RECV_FD, result, 26);
X return(result);
X }
X
Xvoid
Xtzset()
X {
X u_char fnc = TZSET;
X
X sewer();
X write(SEND_FD, &fnc, sizeof fnc);
X }
X
Xstruct tm *
Xlocaltime(tp)
X time_t *tp;
X {
X u_char fnc = LOCALTIME;
X
X sewer();
X write(SEND_FD, &fnc, sizeof fnc);
X write(SEND_FD, tp, sizeof (*tp));
X getb(RECV_FD, &tmtmp, sizeof tmtmp);
X getb(RECV_FD, result, 24);
X tmtmp.tm_zone = result;
X return(&tmtmp);
X }
X
Xstruct tm *
Xgmtime(tp)
X time_t *tp;
X {
X u_char fnc = GMTIME;
X
X sewer();
X write(SEND_FD, &fnc, sizeof fnc);
X write(SEND_FD, tp, sizeof (*tp));
X getb(RECV_FD, &tmtmp, sizeof tmtmp);
X getb(RECV_FD, result, 24);
X tmtmp.tm_zone = result;
X return(&tmtmp);
X }
X
Xstruct tm *
Xofftime(clock, offset)
X time_t *clock;
X long offset;
X {
X u_char fnc = OFFTIME;
X
X sewer();
X write(SEND_FD, &fnc, sizeof fnc);
X write(SEND_FD, clock, sizeof (*clock));
X write(SEND_FD, &offset, sizeof offset);
X getb(RECV_FD, &tmtmp, sizeof tmtmp);
X tmtmp.tm_zone = "";
X return(&tmtmp);
X }
X
Xgetb(f, p, n)
X register int f, n;
X register char *p;
X {
X int i;
X
X while (n)
X {
X i = read(f, p, n);
X if (i <= 0)
X return;
X p += i;
X n -= i;
X }
X }
X
Xsewer()
X {
X register int pid, ourpid = getpid();
X
X if (inited == ourpid)
X return;
X if (inited)
X {
X close(SEND_FD);
X close(RECV_FD);
X }
X pipe(W);
X pipe(R);
X pid = vfork();
X if (pid == 0)
X { /* child */
X dup2(W[0], 0); /* parent write side to our stdin */
X dup2(R[1], 1); /* parent read side to our stdout */
X close(SEND_FD); /* copies made, close the... */
X close(RECV_FD); /* originals now */
X execl("/usr/lib/ctimed", "ctimed", 0);
X _exit(EX_OSFILE);
X }
X if (pid == -1)
X abort(); /* nothing else really to do */
X close(W[0]); /* close read side of SEND channel */
X close(R[1]); /* close write side of RECV channel */
X inited = ourpid; /* don't do this again in this proc */
X }
SHAR_EOF
fi
if test -f 'ctimed.c'
then
echo shar: "will not over-write existing file 'ctimed.c'"
else
sed 's/^X//' << \SHAR_EOF > 'ctimed.c'
X#include <signal.h>
X#include <stdio.h>
X#include <setjmp.h>
X#include <sys/ioctl.h>
X#include <sys/types.h>
X#include <sys/time.h>
X
X#define CTIME 1
X#define ASCTIME 2
X#define TZSET 3
X#define LOCALTIME 4
X#define GMTIME 5
X#define OFFTIME 6
X
Xextern struct tm *offtime();
X
X jmp_buf env;
X char *cp, junk[52];
X long l, off;
X int timeout();
X struct tm tmtmp, *tp;
X
Xmain()
X {
X register int i;
X struct itimerval it;
X u_char c;
X
X signal(SIGPIPE, SIG_DFL);
X for (i = getdtablesize(); i > 2; i--)
X close(i);
X/*
X * Need a timer running while we disassociate from the control terminal
X * in case of a modem line which has lost carrier.
X*/
X timerclear(&it.it_interval);
X it.it_value.tv_sec = 5;
X it.it_value.tv_usec = 0;
X signal(SIGALRM, timeout);
X setitimer(ITIMER_REAL, &it, (struct itimerval *) NULL);
X if (setjmp(env) == 0)
X {
X i = open("/dev/tty", 0);
X if (i >= 0)
X {
X ioctl(i, TIOCNOTTY, NULL);
X close(i);
X }
X }
X signal(SIGALRM, SIG_IGN);
X timerclear(&it.it_interval);
X timerclear(&it.it_value);
X setitimer(ITIMER_REAL, &it, (struct itimerval *) NULL);
X
X while (read(fileno(stdin), &c, 1) == 1)
X {
X switch (c)
X {
X case CTIME:
X l = 0L;
X getb(fileno(stdin), &l, sizeof l);
X cp = ctime(&l);
X write(fileno(stdout), cp, 26);
X break;
X case ASCTIME:
X getb(fileno(stdin), &tmtmp, sizeof tmtmp);
X cp = asctime(&tmtmp);
X write(fileno(stdout), cp, 26);
X break;
X case TZSET:
X (void) tzset();
X break;
X case LOCALTIME:
X l = 0L;
X getb(fileno(stdin), &l, sizeof l);
X tp = localtime(&l);
X write(fileno(stdout), tp, sizeof (*tp));
X strcpy(junk, tp->tm_zone);
X junk[24] = '\0';
X write(fileno(stdout), junk, 24);
X break;
X case GMTIME:
X l = 0L;
X getb(fileno(stdin), &l, sizeof l);
X tp = gmtime(&l);
X write(fileno(stdout), tp, sizeof (*tp));
X strcpy(junk, tp->tm_zone);
X junk[24] = '\0';
X write(fileno(stdout), junk, 24);
X break;
X case OFFTIME:
X getb(fileno(stdin), &l, sizeof l);
X getb(fileno(stdin), &off, sizeof off);
X tp = offtime(&l, off);
X write(fileno(stdout), tp, sizeof (*tp));
X break;
X default:
X abort("switch");
X }
X }
X }
X
Xgetb(f, p, n)
X int f;
X register char *p;
X register int n;
X {
X register int i;
X
X while (n)
X {
X i = read(f, p, n);
X if (i <= 0)
X return;
X p += i;
X n -= i;
X }
X }
X
Xtimeout()
X {
X
X longjmp(env, 1);
X }
SHAR_EOF
fi
exit 0
# End of shell archive
More information about the Comp.bugs.2bsd
mailing list