This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / usr.sbin / sendmail / src / mci.c
CommitLineData
6f14531a
RG
1/*
2 * Copyright (c) 1983 Eric P. Allman
3 * Copyright (c) 1988, 1993
4 * The Regents of the University of California. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by the University of
17 * California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#ifndef lint
d747e748 36static char sccsid[] = "@(#)mci.c 8.6 (Berkeley) 10/23/93";
6f14531a
RG
37#endif /* not lint */
38
39#include "sendmail.h"
40
41/*
42** Mail Connection Information (MCI) Caching Module.
43**
44** There are actually two separate things cached. The first is
45** the set of all open connections -- these are stored in a
46** (small) list. The second is stored in the symbol table; it
47** has the overall status for all hosts, whether or not there
48** is a connection open currently.
49**
50** There should never be too many connections open (since this
51** could flood the socket table), nor should a connection be
52** allowed to sit idly for too long.
53**
54** MaxMciCache is the maximum number of open connections that
55** will be supported.
56**
57** MciCacheTimeout is the time (in seconds) that a connection
58** is permitted to survive without activity.
59**
60** We actually try any cached connections by sending a NOOP
61** before we use them; if the NOOP fails we close down the
62** connection and reopen it. Note that this means that a
63** server SMTP that doesn't support NOOP will hose the
64** algorithm -- but that doesn't seem too likely.
65*/
66
67MCI **MciCache; /* the open connection cache */
68\f/*
69** MCI_CACHE -- enter a connection structure into the open connection cache
70**
71** This may cause something else to be flushed.
72**
73** Parameters:
74** mci -- the connection to cache.
75**
76** Returns:
77** none.
78*/
79
80mci_cache(mci)
81 register MCI *mci;
82{
83 register MCI **mcislot;
84 extern MCI **mci_scan();
85
86 if (MaxMciCache <= 0)
87 {
88 /* we don't support caching */
89 return;
90 }
91
92 /*
93 ** Find the best slot. This may cause expired connections
94 ** to be closed.
95 */
96
97 mcislot = mci_scan(mci);
98
99 /* if this is already cached, we are done */
100 if (bitset(MCIF_CACHED, mci->mci_flags))
101 return;
102
103 /* otherwise we may have to clear the slot */
104 if (*mcislot != NULL)
105 mci_uncache(mcislot, TRUE);
106
d747e748
JH
107 if (tTd(42, 5))
108 printf("mci_cache: caching %x (%s) in slot %d\n",
109 mci, mci->mci_host, mcislot - MciCache);
110#ifdef LOG
111 if (tTd(91, 100))
112 syslog(LOG_DEBUG, "%s: mci_cache: caching %x (%s) in slot %d",
113 CurEnv->e_id ? CurEnv->e_id : "NOQUEUE",
114 mci, mci->mci_host, mcislot - MciCache);
115#endif
116
6f14531a
RG
117 *mcislot = mci;
118 mci->mci_flags |= MCIF_CACHED;
119}
120\f/*
121** MCI_SCAN -- scan the cache, flush junk, and return best slot
122**
123** Parameters:
124** savemci -- never flush this one. Can be null.
125**
126** Returns:
127** The LRU (or empty) slot.
128*/
129
130MCI **
131mci_scan(savemci)
132 MCI *savemci;
133{
134 time_t now;
135 register MCI **bestmci;
136 register MCI *mci;
137 register int i;
138
139 if (MciCache == NULL)
140 {
141 /* first call */
142 MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache);
143 bzero((char *) MciCache, MaxMciCache * sizeof *MciCache);
144 return (&MciCache[0]);
145 }
146
147 now = curtime();
148 bestmci = &MciCache[0];
149 for (i = 0; i < MaxMciCache; i++)
150 {
151 mci = MciCache[i];
152 if (mci == NULL || mci->mci_state == MCIS_CLOSED)
153 {
154 bestmci = &MciCache[i];
155 continue;
156 }
157 if (mci->mci_lastuse + MciCacheTimeout < now && mci != savemci)
158 {
159 /* connection idle too long -- close it */
160 bestmci = &MciCache[i];
161 mci_uncache(bestmci, TRUE);
162 continue;
163 }
164 if (*bestmci == NULL)
165 continue;
166 if (mci->mci_lastuse < (*bestmci)->mci_lastuse)
167 bestmci = &MciCache[i];
168 }
169 return bestmci;
170}
171\f/*
172** MCI_UNCACHE -- remove a connection from a slot.
173**
174** May close a connection.
175**
176** Parameters:
177** mcislot -- the slot to empty.
178** doquit -- if TRUE, send QUIT protocol on this connection.
179** if FALSE, we are assumed to be in a forked child;
180** all we want to do is close the file(s).
181**
182** Returns:
183** none.
184*/
185
186mci_uncache(mcislot, doquit)
187 register MCI **mcislot;
188 bool doquit;
189{
190 register MCI *mci;
191 extern ENVELOPE BlankEnvelope;
192
193 mci = *mcislot;
194 if (mci == NULL)
195 return;
196 *mcislot = NULL;
197
d747e748
JH
198 if (tTd(42, 5))
199 printf("mci_uncache: uncaching %x (%s) from slot %d (%d)\n",
200 mci, mci->mci_host, mcislot - MciCache, doquit);
201#ifdef LOG
202 if (tTd(91, 100))
203 syslog(LOG_DEBUG, "%s: mci_uncache: uncaching %x (%s) from slot %d (%d)",
204 CurEnv->e_id ? CurEnv->e_id : "NOQUEUE",
205 mci, mci->mci_host, mcislot - MciCache, doquit);
206#endif
207
6f14531a
RG
208 if (doquit)
209 {
210 message("Closing connection to %s", mci->mci_host);
211
212 mci->mci_flags &= ~MCIF_CACHED;
213
214 /* only uses the envelope to flush the transcript file */
215 if (mci->mci_state != MCIS_CLOSED)
216 smtpquit(mci->mci_mailer, mci, &BlankEnvelope);
217#ifdef XLA
218 xla_host_end(mci->mci_host);
219#endif
220 }
221 else
222 {
223 if (mci->mci_in != NULL)
224 xfclose(mci->mci_in, "mci_uncache", "mci_in");
225 if (mci->mci_out != NULL)
226 xfclose(mci->mci_out, "mci_uncache", "mci_out");
227 mci->mci_in = mci->mci_out = NULL;
228 mci->mci_state = MCIS_CLOSED;
229 mci->mci_exitstat = EX_OK;
230 mci->mci_errno = 0;
231 mci->mci_flags = 0;
232 }
233}
234\f/*
235** MCI_FLUSH -- flush the entire cache
236**
237** Parameters:
238** doquit -- if TRUE, send QUIT protocol.
239** if FALSE, just close the connection.
240** allbut -- but leave this one open.
241**
242** Returns:
243** none.
244*/
245
246mci_flush(doquit, allbut)
247 bool doquit;
248 MCI *allbut;
249{
250 register int i;
251
252 if (MciCache == NULL)
253 return;
254
255 for (i = 0; i < MaxMciCache; i++)
256 if (allbut != MciCache[i])
257 mci_uncache(&MciCache[i], doquit);
258}
259\f/*
260** MCI_GET -- get information about a particular host
261*/
262
263MCI *
264mci_get(host, m)
265 char *host;
266 MAILER *m;
267{
268 register MCI *mci;
269 register STAB *s;
270
271#ifdef DAEMON
272 extern SOCKADDR CurHostAddr;
273
274 /* clear CurHostAddr so we don't get a bogus address with this name */
275 bzero(&CurHostAddr, sizeof CurHostAddr);
d747e748 276#endif
6f14531a
RG
277
278 s = stab(host, ST_MCI + m->m_mno, ST_ENTER);
279 mci = &s->s_mci;
280 mci->mci_host = s->s_name;
281
282 if (tTd(42, 2))
283 {
284 printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n",
285 host, m->m_name, mci->mci_state, mci->mci_flags,
286 mci->mci_exitstat, mci->mci_errno);
287 }
288
289 if (mci->mci_state == MCIS_OPEN)
290 {
291 /* poke the connection to see if it's still alive */
292 smtpprobe(mci);
293
294 /* reset the stored state in the event of a timeout */
295 if (mci->mci_state != MCIS_OPEN)
296 {
297 mci->mci_errno = 0;
298 mci->mci_exitstat = EX_OK;
299 mci->mci_state = MCIS_CLOSED;
300 }
301 }
302
303 return mci;
304}
305\f/*
306** MCI_DUMP -- dump the contents of an MCI structure.
307**
308** Parameters:
309** mci -- the MCI structure to dump.
310**
311** Returns:
312** none.
313**
314** Side Effects:
315** none.
316*/
317
d747e748 318mci_dump(mci, logit)
6f14531a 319 register MCI *mci;
d747e748 320 bool logit;
6f14531a 321{
d747e748
JH
322 register char *p;
323 char *sep;
324 char buf[1000];
6f14531a
RG
325 extern char *ctime();
326
d747e748
JH
327 sep = logit ? " " : "\n\t";
328 p = buf;
329 sprintf(p, "MCI@%x: ", mci);
330 p += strlen(p);
6f14531a
RG
331 if (mci == NULL)
332 {
d747e748
JH
333 sprintf(p, "NULL");
334 goto printit;
6f14531a 335 }
d747e748 336 sprintf(p, "flags=%o, errno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s",
3a363396 337 mci->mci_flags, mci->mci_errno, mci->mci_herrno,
d747e748
JH
338 mci->mci_exitstat, mci->mci_state, mci->mci_pid, sep);
339 p += strlen(p);
340 sprintf(p, "maxsize=%ld, phase=%s, mailer=%s,%s",
3a363396 341 mci->mci_maxsize,
6f14531a 342 mci->mci_phase == NULL ? "NULL" : mci->mci_phase,
d747e748
JH
343 mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name,
344 sep);
345 p += strlen(p);
346 sprintf(p, "host=%s, lastuse=%s",
6f14531a
RG
347 mci->mci_host == NULL ? "NULL" : mci->mci_host,
348 ctime(&mci->mci_lastuse));
d747e748
JH
349printit:
350 if (logit)
351 syslog(LOG_INFO, "%s", buf);
352 else
353 printf("%s\n", buf);
354}
355\f/*
356** MCI_DUMP_ALL -- print the entire MCI cache
357**
358** Parameters:
359** logit -- if set, log the result instead of printing
360** to stdout.
361**
362** Returns:
363** none.
364*/
365
366mci_dump_all(logit)
367 bool logit;
368{
369 register int i;
370
371 for (i = 0; i < MaxMciCache; i++)
372 mci_dump(MciCache[i], logit);
6f14531a 373}