xref: /csrg-svn/usr.sbin/sendmail/src/mci.c (revision 54967)
1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988 Regents of the University of California.
4  * All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)mci.c	5.2 (Berkeley) 07/11/92";
11 #endif /* not lint */
12 
13 #include "sendmail.h"
14 
15 /*
16 **  Mail Connection Information (MCI) Caching Module.
17 **
18 **	There are actually two separate things cached.  The first is
19 **	the set of all open connections -- these are stored in a
20 **	(small) list.  The second is stored in the symbol table; it
21 **	has the overall status for all hosts, whether or not there
22 **	is a connection open currently.
23 **
24 **	There should never be too many connections open (since this
25 **	could flood the socket table), nor should a connection be
26 **	allowed to sit idly for too long.
27 **
28 **	MaxMciCache is the maximum number of open connections that
29 **	will be supported.
30 **
31 **	MciCacheTimeout is the time (in seconds) that a connection
32 **	is permitted to survive without activity.
33 **
34 **	We actually try any cached connections by sending a NOOP
35 **	before we use them; if the NOOP fails we close down the
36 **	connection and reopen it.  Note that this means that a
37 **	server SMTP that doesn't support NOOP will hose the
38 **	algorithm -- but that doesn't seem too likely.
39 */
40 
41 MCI	**MciCache;		/* the open connection cache */
42 /*
43 **  MCI_CACHE -- enter a connection structure into the open connection cache
44 **
45 **	This may cause something else to be flushed.
46 **
47 **	Parameters:
48 **		mci -- the connection to cache.
49 **
50 **	Returns:
51 **		none.
52 */
53 
54 mci_cache(mci)
55 	register MCI *mci;
56 {
57 	register MCI **mcislot;
58 	extern MCI **mci_scan();
59 
60 	if (MaxMciCache <= 0)
61 	{
62 		/* we don't support caching */
63 		return;
64 	}
65 
66 	/*
67 	**  Find the best slot.  This may cause expired connections
68 	**  to be closed.
69 	*/
70 
71 	mcislot = mci_scan(mci);
72 
73 	/* if this is already cached, we are done */
74 	if (bitset(MCIF_CACHED, mci->mci_flags))
75 		return;
76 
77 	/* otherwise we may have to clear the slot */
78 	if (*mcislot != NULL)
79 		mci_uncache(mcislot);
80 
81 	*mcislot = mci;
82 	mci->mci_flags |= MCIF_CACHED;
83 }
84 /*
85 **  MCI_SCAN -- scan the cache, flush junk, and return best slot
86 **
87 **	Parameters:
88 **		savemci -- never flush this one.  Can be null.
89 **
90 **	Returns:
91 **		The LRU (or empty) slot.
92 */
93 
94 MCI **
95 mci_scan(savemci)
96 	MCI *savemci;
97 {
98 	time_t now;
99 	register MCI **bestmci;
100 	register MCI *mci;
101 	register int i;
102 
103 	if (MciCache == NULL)
104 	{
105 		/* first call */
106 		MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache);
107 		return (&MciCache[0]);
108 	}
109 
110 	now = curtime();
111 	bestmci = &MciCache[0];
112 	for (i = 0; i < MaxMciCache; i++)
113 	{
114 		mci = MciCache[i];
115 		if (mci == NULL || mci->mci_state == MCIS_CLOSED)
116 		{
117 			bestmci = &MciCache[i];
118 			continue;
119 		}
120 		if (mci->mci_lastuse + MciCacheTimeout < now && mci != savemci)
121 		{
122 			/* connection idle too long -- close it */
123 			bestmci = &MciCache[i];
124 			mci_uncache(bestmci);
125 			continue;
126 		}
127 		if (*bestmci == NULL)
128 			continue;
129 		if (mci->mci_lastuse < (*bestmci)->mci_lastuse)
130 			bestmci = &MciCache[i];
131 	}
132 	return bestmci;
133 }
134 /*
135 **  MCI_UNCACHE -- remove a connection from a slot.
136 **
137 **	May close a connection.
138 **
139 **	Parameters:
140 **		mcislot -- the slot to empty.
141 **
142 **	Returns:
143 **		none.
144 */
145 
146 mci_uncache(mcislot)
147 	register MCI **mcislot;
148 {
149 	register MCI *mci;
150 	extern ENVELOPE *BlankEnvelope;
151 
152 	mci = *mcislot;
153 	if (mci == NULL)
154 		return;
155 	*mcislot = NULL;
156 	mci->mci_flags &= ~MCIF_CACHED;
157 
158 	/* only uses the envelope to flush the transcript file */
159 	if (mci->mci_state != MCIS_CLOSED)
160 		smtpquit(mci->mci_mailer, mci, &BlankEnvelope);
161 }
162 /*
163 **  MCI_FLUSH -- flush the entire cache
164 */
165 
166 mci_flush()
167 {
168 	register int i;
169 
170 	if (MciCache == NULL)
171 		return;
172 
173 	for (i = 0; i < MaxMciCache; i++)
174 		mci_uncache(&MciCache[i]);
175 }
176 /*
177 **  MCI_GET -- get information about a particular host
178 */
179 
180 MCI *
181 mci_get(host, m)
182 	char *host;
183 	MAILER *m;
184 {
185 	register MCI *mci;
186 
187 	mci = &(stab(host, ST_MCI + m->m_mno, ST_ENTER))->s_mci;
188 
189 	if (tTd(42, 2))
190 	{
191 		printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n",
192 			host, m->m_name, mci->mci_state, mci->mci_flags,
193 			mci->mci_exitstat, mci->mci_errno);
194 	}
195 
196 	/* try poking this to see if it is still usable */
197 	switch (mci->mci_state)
198 	{
199 	  case MCIS_OPEN:
200 		smtpnoop(mci);
201 		break;
202 	}
203 
204 	return mci;
205 }
206