xref: /csrg-svn/usr.sbin/sendmail/src/mci.c (revision 58680)
154963Seric /*
254963Seric  * Copyright (c) 1983 Eric P. Allman
354963Seric  * Copyright (c) 1988 Regents of the University of California.
454963Seric  * All rights reserved.
554963Seric  *
654963Seric  * %sccs.include.redist.c%
754963Seric  */
854963Seric 
954963Seric #ifndef lint
10*58680Seric static char sccsid[] = "@(#)mci.c	6.6 (Berkeley) 03/16/93";
1154963Seric #endif /* not lint */
1254963Seric 
1354963Seric #include "sendmail.h"
1454963Seric 
1554963Seric /*
1654967Seric **  Mail Connection Information (MCI) Caching Module.
1754967Seric **
1854967Seric **	There are actually two separate things cached.  The first is
1954967Seric **	the set of all open connections -- these are stored in a
2054967Seric **	(small) list.  The second is stored in the symbol table; it
2154967Seric **	has the overall status for all hosts, whether or not there
2254967Seric **	is a connection open currently.
2354967Seric **
2454967Seric **	There should never be too many connections open (since this
2554967Seric **	could flood the socket table), nor should a connection be
2654967Seric **	allowed to sit idly for too long.
2754967Seric **
2854967Seric **	MaxMciCache is the maximum number of open connections that
2954967Seric **	will be supported.
3054967Seric **
3154967Seric **	MciCacheTimeout is the time (in seconds) that a connection
3254967Seric **	is permitted to survive without activity.
3354967Seric **
3454967Seric **	We actually try any cached connections by sending a NOOP
3554967Seric **	before we use them; if the NOOP fails we close down the
3654967Seric **	connection and reopen it.  Note that this means that a
3754967Seric **	server SMTP that doesn't support NOOP will hose the
3854967Seric **	algorithm -- but that doesn't seem too likely.
3954967Seric */
4054967Seric 
4154967Seric MCI	**MciCache;		/* the open connection cache */
4254967Seric /*
4354963Seric **  MCI_CACHE -- enter a connection structure into the open connection cache
4454963Seric **
4554963Seric **	This may cause something else to be flushed.
4654963Seric **
4754963Seric **	Parameters:
4854963Seric **		mci -- the connection to cache.
4954963Seric **
5054963Seric **	Returns:
5154963Seric **		none.
5254963Seric */
5354963Seric 
5454963Seric mci_cache(mci)
5554967Seric 	register MCI *mci;
5654963Seric {
5754967Seric 	register MCI **mcislot;
5854967Seric 	extern MCI **mci_scan();
5954963Seric 
6054963Seric 	if (MaxMciCache <= 0)
6154963Seric 	{
6254963Seric 		/* we don't support caching */
6354963Seric 		return;
6454963Seric 	}
6554963Seric 
6654963Seric 	/*
6754963Seric 	**  Find the best slot.  This may cause expired connections
6854963Seric 	**  to be closed.
6954963Seric 	*/
7054963Seric 
7154963Seric 	mcislot = mci_scan(mci);
7254963Seric 
7354963Seric 	/* if this is already cached, we are done */
7454963Seric 	if (bitset(MCIF_CACHED, mci->mci_flags))
7554963Seric 		return;
7654963Seric 
7754963Seric 	/* otherwise we may have to clear the slot */
7854963Seric 	if (*mcislot != NULL)
7958082Seric 		mci_uncache(mcislot, TRUE);
8054963Seric 
8154963Seric 	*mcislot = mci;
8254963Seric 	mci->mci_flags |= MCIF_CACHED;
8354963Seric }
8454963Seric /*
8554963Seric **  MCI_SCAN -- scan the cache, flush junk, and return best slot
8654963Seric **
8754963Seric **	Parameters:
8854963Seric **		savemci -- never flush this one.  Can be null.
8954963Seric **
9054963Seric **	Returns:
9154963Seric **		The LRU (or empty) slot.
9254963Seric */
9354963Seric 
9454967Seric MCI **
9554963Seric mci_scan(savemci)
9654967Seric 	MCI *savemci;
9754963Seric {
9854963Seric 	time_t now;
9954967Seric 	register MCI **bestmci;
10054967Seric 	register MCI *mci;
10154963Seric 	register int i;
10254963Seric 
10354963Seric 	if (MciCache == NULL)
10454963Seric 	{
10554963Seric 		/* first call */
10654967Seric 		MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache);
10756215Seric 		bzero((char *) MciCache, MaxMciCache * sizeof *MciCache);
10854963Seric 		return (&MciCache[0]);
10954963Seric 	}
11054963Seric 
11154963Seric 	now = curtime();
11254963Seric 	bestmci = &MciCache[0];
11354963Seric 	for (i = 0; i < MaxMciCache; i++)
11454963Seric 	{
11554963Seric 		mci = MciCache[i];
11654963Seric 		if (mci == NULL || mci->mci_state == MCIS_CLOSED)
11754963Seric 		{
11854963Seric 			bestmci = &MciCache[i];
11954963Seric 			continue;
12054963Seric 		}
12154963Seric 		if (mci->mci_lastuse + MciCacheTimeout < now && mci != savemci)
12254963Seric 		{
12354963Seric 			/* connection idle too long -- close it */
12454963Seric 			bestmci = &MciCache[i];
12558082Seric 			mci_uncache(bestmci, TRUE);
12654963Seric 			continue;
12754963Seric 		}
12854963Seric 		if (*bestmci == NULL)
12954963Seric 			continue;
13054963Seric 		if (mci->mci_lastuse < (*bestmci)->mci_lastuse)
13154963Seric 			bestmci = &MciCache[i];
13254963Seric 	}
13354963Seric 	return bestmci;
13454963Seric }
13554963Seric /*
13654963Seric **  MCI_UNCACHE -- remove a connection from a slot.
13754963Seric **
13854963Seric **	May close a connection.
13954963Seric **
14054963Seric **	Parameters:
14154963Seric **		mcislot -- the slot to empty.
14258082Seric **		doquit -- if TRUE, send QUIT protocol on this connection.
14358082Seric **			  if FALSE, we are assumed to be in a forked child;
14458082Seric **				all we want to do is close the file(s).
14554963Seric **
14654963Seric **	Returns:
14754963Seric **		none.
14854963Seric */
14954963Seric 
15058082Seric mci_uncache(mcislot, doquit)
15154967Seric 	register MCI **mcislot;
15258082Seric 	bool doquit;
15354963Seric {
15454967Seric 	register MCI *mci;
15555020Seric 	extern ENVELOPE BlankEnvelope;
15654963Seric 
15754963Seric 	mci = *mcislot;
15854963Seric 	if (mci == NULL)
15954963Seric 		return;
16054963Seric 	*mcislot = NULL;
16154963Seric 
16258082Seric 	if (doquit)
16358082Seric 	{
16458151Seric 		message("Closing connection to %s", mci->mci_host);
16555467Seric 
16658082Seric 		mci->mci_flags &= ~MCIF_CACHED;
16758082Seric 
16858082Seric 		/* only uses the envelope to flush the transcript file */
16958082Seric 		if (mci->mci_state != MCIS_CLOSED)
17058082Seric 			smtpquit(mci->mci_mailer, mci, &BlankEnvelope);
17158082Seric 	}
17258082Seric 	else
17358082Seric 	{
17458082Seric 		if (mci->mci_in != NULL)
175*58680Seric 			xfclose(mci->mci_in, "mci_uncache", "mci_in");
17658082Seric 		if (mci->mci_out != NULL)
177*58680Seric 			xfclose(mci->mci_out, "mci_uncache", "mci_out");
17858082Seric 		mci->mci_in = mci->mci_out = NULL;
17958082Seric 		mci->mci_state = MCIS_CLOSED;
18058082Seric 		mci->mci_exitstat = EX_OK;
18158082Seric 		mci->mci_errno = 0;
18258082Seric 		mci->mci_flags = 0;
18358082Seric 	}
18454963Seric }
18554963Seric /*
18654963Seric **  MCI_FLUSH -- flush the entire cache
18758082Seric **
18858082Seric **	Parameters:
18958082Seric **		doquit -- if TRUE, send QUIT protocol.
19058082Seric **			  if FALSE, just close the connection.
19158082Seric **		allbut -- but leave this one open.
19258082Seric **
19358082Seric **	Returns:
19458082Seric **		none.
19554963Seric */
19654963Seric 
19758082Seric mci_flush(doquit, allbut)
19858082Seric 	bool doquit;
19958082Seric 	MCI *allbut;
20054963Seric {
20154963Seric 	register int i;
20254963Seric 
20354963Seric 	if (MciCache == NULL)
20454963Seric 		return;
20554963Seric 
20654963Seric 	for (i = 0; i < MaxMciCache; i++)
20758082Seric 		if (allbut != MciCache[i])
20858082Seric 			mci_uncache(&MciCache[i], doquit);
20954963Seric }
21054963Seric /*
21154963Seric **  MCI_GET -- get information about a particular host
21254963Seric */
21354963Seric 
21454967Seric MCI *
21554963Seric mci_get(host, m)
21654963Seric 	char *host;
21754963Seric 	MAILER *m;
21854963Seric {
21954967Seric 	register MCI *mci;
22055467Seric 	register STAB *s;
22154967Seric 
22255467Seric 	s = stab(host, ST_MCI + m->m_mno, ST_ENTER);
22355467Seric 	mci = &s->s_mci;
22455467Seric 	mci->mci_host = s->s_name;
22554967Seric 
22654967Seric 	if (tTd(42, 2))
22754967Seric 	{
22854967Seric 		printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n",
22954967Seric 			host, m->m_name, mci->mci_state, mci->mci_flags,
23054967Seric 			mci->mci_exitstat, mci->mci_errno);
23154967Seric 	}
23254967Seric 
23357977Seric 	if (mci->mci_state == MCIS_OPEN)
23454967Seric 	{
23557977Seric 		/* poke the connection to see if it's still alive */
23654967Seric 		smtpnoop(mci);
23757977Seric 
23857977Seric 		/* reset the stored state in the event of a timeout */
23957977Seric 		if (mci->mci_state != MCIS_OPEN)
24057977Seric 		{
24157977Seric 			mci->mci_errno = 0;
24257977Seric 			mci->mci_exitstat = EX_OK;
24357977Seric 			mci->mci_state = MCIS_CLOSED;
24457977Seric 		}
24554967Seric 	}
24654967Seric 
24754967Seric 	return mci;
24854963Seric }
24957380Seric /*
25057380Seric **  MCI_DUMP -- dump the contents of an MCI structure.
25157380Seric **
25257380Seric **	Parameters:
25357380Seric **		mci -- the MCI structure to dump.
25457380Seric **
25557380Seric **	Returns:
25657380Seric **		none.
25757380Seric **
25857380Seric **	Side Effects:
25957380Seric **		none.
26057380Seric */
26157380Seric 
26257380Seric mci_dump(mci)
26357380Seric 	register MCI *mci;
26457380Seric {
26557380Seric 	extern char *ctime();
26657380Seric 
26757380Seric 	printf("MCI@%x: ", mci);
26857380Seric 	if (mci == NULL)
26957380Seric 	{
27057380Seric 		printf("NULL\n");
27157380Seric 		return;
27257380Seric 	}
27357380Seric 	printf("flags=%o, errno=%d, exitstat=%d, state=%d, pid=%d,\n",
27457380Seric 		mci->mci_flags, mci->mci_errno, mci->mci_exitstat,
27557380Seric 		mci->mci_state, mci->mci_pid);
27657380Seric 	printf("\tphase=%s, mailer=%s,\n",
27757380Seric 		mci->mci_phase == NULL ? "NULL" : mci->mci_phase,
27857380Seric 		mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name);
27957380Seric 	printf("\thost=%s, lastuse=%s\n",
28057380Seric 		mci->mci_host == NULL ? "NULL" : mci->mci_host,
28157380Seric 		ctime(&mci->mci_lastuse));
28257380Seric }
283