xref: /original-bsd/usr.sbin/sendmail/src/mci.c (revision dc4562f1)
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	6.2 (Berkeley) 01/01/93";
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 		bzero((char *) MciCache, MaxMciCache * sizeof *MciCache);
108 		return (&MciCache[0]);
109 	}
110 
111 	now = curtime();
112 	bestmci = &MciCache[0];
113 	for (i = 0; i < MaxMciCache; i++)
114 	{
115 		mci = MciCache[i];
116 		if (mci == NULL || mci->mci_state == MCIS_CLOSED)
117 		{
118 			bestmci = &MciCache[i];
119 			continue;
120 		}
121 		if (mci->mci_lastuse + MciCacheTimeout < now && mci != savemci)
122 		{
123 			/* connection idle too long -- close it */
124 			bestmci = &MciCache[i];
125 			mci_uncache(bestmci);
126 			continue;
127 		}
128 		if (*bestmci == NULL)
129 			continue;
130 		if (mci->mci_lastuse < (*bestmci)->mci_lastuse)
131 			bestmci = &MciCache[i];
132 	}
133 	return bestmci;
134 }
135 /*
136 **  MCI_UNCACHE -- remove a connection from a slot.
137 **
138 **	May close a connection.
139 **
140 **	Parameters:
141 **		mcislot -- the slot to empty.
142 **
143 **	Returns:
144 **		none.
145 */
146 
147 mci_uncache(mcislot)
148 	register MCI **mcislot;
149 {
150 	register MCI *mci;
151 	extern ENVELOPE BlankEnvelope;
152 
153 	mci = *mcislot;
154 	if (mci == NULL)
155 		return;
156 	*mcislot = NULL;
157 	mci->mci_flags &= ~MCIF_CACHED;
158 
159 	message(Arpa_Info, "Closing connection to %s", mci->mci_host);
160 
161 	/* only uses the envelope to flush the transcript file */
162 	if (mci->mci_state != MCIS_CLOSED)
163 		smtpquit(mci->mci_mailer, mci, &BlankEnvelope);
164 }
165 /*
166 **  MCI_FLUSH -- flush the entire cache
167 */
168 
169 mci_flush()
170 {
171 	register int i;
172 
173 	if (MciCache == NULL)
174 		return;
175 
176 	for (i = 0; i < MaxMciCache; i++)
177 		mci_uncache(&MciCache[i]);
178 }
179 /*
180 **  MCI_GET -- get information about a particular host
181 */
182 
183 MCI *
184 mci_get(host, m)
185 	char *host;
186 	MAILER *m;
187 {
188 	register MCI *mci;
189 	register STAB *s;
190 
191 	s = stab(host, ST_MCI + m->m_mno, ST_ENTER);
192 	mci = &s->s_mci;
193 	mci->mci_host = s->s_name;
194 
195 	if (tTd(42, 2))
196 	{
197 		printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n",
198 			host, m->m_name, mci->mci_state, mci->mci_flags,
199 			mci->mci_exitstat, mci->mci_errno);
200 	}
201 
202 	/* try poking this to see if it is still usable */
203 	switch (mci->mci_state)
204 	{
205 	  case MCIS_OPEN:
206 		smtpnoop(mci);
207 		break;
208 	}
209 
210 	return mci;
211 }
212 /*
213 **  MCI_DUMP -- dump the contents of an MCI structure.
214 **
215 **	Parameters:
216 **		mci -- the MCI structure to dump.
217 **
218 **	Returns:
219 **		none.
220 **
221 **	Side Effects:
222 **		none.
223 */
224 
225 mci_dump(mci)
226 	register MCI *mci;
227 {
228 	extern char *ctime();
229 
230 	printf("MCI@%x: ", mci);
231 	if (mci == NULL)
232 	{
233 		printf("NULL\n");
234 		return;
235 	}
236 	printf("flags=%o, errno=%d, exitstat=%d, state=%d, pid=%d,\n",
237 		mci->mci_flags, mci->mci_errno, mci->mci_exitstat,
238 		mci->mci_state, mci->mci_pid);
239 	printf("\tphase=%s, mailer=%s,\n",
240 		mci->mci_phase == NULL ? "NULL" : mci->mci_phase,
241 		mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name);
242 	printf("\thost=%s, lastuse=%s\n",
243 		mci->mci_host == NULL ? "NULL" : mci->mci_host,
244 		ctime(&mci->mci_lastuse));
245 }
246