xref: /original-bsd/usr.sbin/sendmail/src/mci.c (revision 5133e8a4)
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.4 (Berkeley) 07/20/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 	message(Arpa_Info, "Closing connection to %s", mci->mci_host);
159 
160 	/* only uses the envelope to flush the transcript file */
161 	if (mci->mci_state != MCIS_CLOSED)
162 		smtpquit(mci->mci_mailer, mci, &BlankEnvelope);
163 }
164 /*
165 **  MCI_FLUSH -- flush the entire cache
166 */
167 
168 mci_flush()
169 {
170 	register int i;
171 
172 	if (MciCache == NULL)
173 		return;
174 
175 	for (i = 0; i < MaxMciCache; i++)
176 		mci_uncache(&MciCache[i]);
177 }
178 /*
179 **  MCI_GET -- get information about a particular host
180 */
181 
182 MCI *
183 mci_get(host, m)
184 	char *host;
185 	MAILER *m;
186 {
187 	register MCI *mci;
188 	register STAB *s;
189 
190 	s = stab(host, ST_MCI + m->m_mno, ST_ENTER);
191 	mci = &s->s_mci;
192 	mci->mci_host = s->s_name;
193 
194 	if (tTd(42, 2))
195 	{
196 		printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n",
197 			host, m->m_name, mci->mci_state, mci->mci_flags,
198 			mci->mci_exitstat, mci->mci_errno);
199 	}
200 
201 	/* try poking this to see if it is still usable */
202 	switch (mci->mci_state)
203 	{
204 	  case MCIS_OPEN:
205 		smtpnoop(mci);
206 		break;
207 	}
208 
209 	return mci;
210 }
211