xref: /freebsd/contrib/sendmail/src/mci.c (revision c46d91b759c7a461806a7f6e6efccc404d2c21b8)
1c2aa98e2SPeter Wemm /*
23299c2f1SGregory Neil Shapiro  * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
33299c2f1SGregory Neil Shapiro  *	All rights reserved.
4c2aa98e2SPeter Wemm  * Copyright (c) 1995-1997 Eric P. Allman.  All rights reserved.
5c2aa98e2SPeter Wemm  * Copyright (c) 1988, 1993
6c2aa98e2SPeter Wemm  *	The Regents of the University of California.  All rights reserved.
7c2aa98e2SPeter Wemm  *
8c2aa98e2SPeter Wemm  * By using this file, you agree to the terms and conditions set
9c2aa98e2SPeter Wemm  * forth in the LICENSE file which can be found at the top level of
10c2aa98e2SPeter Wemm  * the sendmail distribution.
11c2aa98e2SPeter Wemm  *
12c2aa98e2SPeter Wemm  */
13c2aa98e2SPeter Wemm 
14c2aa98e2SPeter Wemm #ifndef lint
15c46d91b7SGregory Neil Shapiro static char id[] = "@(#)$Id: mci.c,v 8.133.10.7 2000/12/12 00:39:34 ca Exp $";
163299c2f1SGregory Neil Shapiro #endif /* ! lint */
17c2aa98e2SPeter Wemm 
183299c2f1SGregory Neil Shapiro /* $FreeBSD$ */
193299c2f1SGregory Neil Shapiro 
203299c2f1SGregory Neil Shapiro #include <sendmail.h>
213299c2f1SGregory Neil Shapiro 
223299c2f1SGregory Neil Shapiro 
233299c2f1SGregory Neil Shapiro #if NETINET || NETINET6
24c2aa98e2SPeter Wemm # include <arpa/inet.h>
253299c2f1SGregory Neil Shapiro #endif /* NETINET || NETINET6 */
263299c2f1SGregory Neil Shapiro 
27c2aa98e2SPeter Wemm #include <dirent.h>
28c2aa98e2SPeter Wemm 
293299c2f1SGregory Neil Shapiro static int	mci_generate_persistent_path __P((const char *, char *,
303299c2f1SGregory Neil Shapiro 						  int, bool));
313299c2f1SGregory Neil Shapiro static bool	mci_load_persistent __P((MCI *));
323299c2f1SGregory Neil Shapiro static void	mci_uncache __P((MCI **, bool));
333299c2f1SGregory Neil Shapiro static int	mci_lock_host_statfile __P((MCI *));
343299c2f1SGregory Neil Shapiro static int	mci_read_persistent __P((FILE *, MCI *));
353299c2f1SGregory Neil Shapiro 
36c2aa98e2SPeter Wemm /*
37c2aa98e2SPeter Wemm **  Mail Connection Information (MCI) Caching Module.
38c2aa98e2SPeter Wemm **
39c2aa98e2SPeter Wemm **	There are actually two separate things cached.  The first is
40c2aa98e2SPeter Wemm **	the set of all open connections -- these are stored in a
41c2aa98e2SPeter Wemm **	(small) list.  The second is stored in the symbol table; it
42c2aa98e2SPeter Wemm **	has the overall status for all hosts, whether or not there
43c2aa98e2SPeter Wemm **	is a connection open currently.
44c2aa98e2SPeter Wemm **
45c2aa98e2SPeter Wemm **	There should never be too many connections open (since this
46c2aa98e2SPeter Wemm **	could flood the socket table), nor should a connection be
47c2aa98e2SPeter Wemm **	allowed to sit idly for too long.
48c2aa98e2SPeter Wemm **
49c2aa98e2SPeter Wemm **	MaxMciCache is the maximum number of open connections that
50c2aa98e2SPeter Wemm **	will be supported.
51c2aa98e2SPeter Wemm **
52c2aa98e2SPeter Wemm **	MciCacheTimeout is the time (in seconds) that a connection
53c2aa98e2SPeter Wemm **	is permitted to survive without activity.
54c2aa98e2SPeter Wemm **
55c2aa98e2SPeter Wemm **	We actually try any cached connections by sending a NOOP
56c2aa98e2SPeter Wemm **	before we use them; if the NOOP fails we close down the
57c2aa98e2SPeter Wemm **	connection and reopen it.  Note that this means that a
58c2aa98e2SPeter Wemm **	server SMTP that doesn't support NOOP will hose the
59c2aa98e2SPeter Wemm **	algorithm -- but that doesn't seem too likely.
60c2aa98e2SPeter Wemm **
61c2aa98e2SPeter Wemm **	The persistent MCI code is donated by Mark Lovell and Paul
62c2aa98e2SPeter Wemm **	Vixie.  It is based on the long term host status code in KJS
63c2aa98e2SPeter Wemm **	written by Paul but has been adapted by Mark to fit into the
64c2aa98e2SPeter Wemm **	MCI structure.
65c2aa98e2SPeter Wemm */
66c2aa98e2SPeter Wemm 
673299c2f1SGregory Neil Shapiro static MCI	**MciCache;		/* the open connection cache */
68c2aa98e2SPeter Wemm 
69c2aa98e2SPeter Wemm /*
70c2aa98e2SPeter Wemm **  MCI_CACHE -- enter a connection structure into the open connection cache
71c2aa98e2SPeter Wemm **
72c2aa98e2SPeter Wemm **	This may cause something else to be flushed.
73c2aa98e2SPeter Wemm **
74c2aa98e2SPeter Wemm **	Parameters:
75c2aa98e2SPeter Wemm **		mci -- the connection to cache.
76c2aa98e2SPeter Wemm **
77c2aa98e2SPeter Wemm **	Returns:
78c2aa98e2SPeter Wemm **		none.
79c2aa98e2SPeter Wemm */
80c2aa98e2SPeter Wemm 
81c2aa98e2SPeter Wemm void
82c2aa98e2SPeter Wemm mci_cache(mci)
83c2aa98e2SPeter Wemm 	register MCI *mci;
84c2aa98e2SPeter Wemm {
85c2aa98e2SPeter Wemm 	register MCI **mcislot;
86c2aa98e2SPeter Wemm 
87c2aa98e2SPeter Wemm 	/*
88c2aa98e2SPeter Wemm 	**  Find the best slot.  This may cause expired connections
89c2aa98e2SPeter Wemm 	**  to be closed.
90c2aa98e2SPeter Wemm 	*/
91c2aa98e2SPeter Wemm 
92c2aa98e2SPeter Wemm 	mcislot = mci_scan(mci);
93c2aa98e2SPeter Wemm 	if (mcislot == NULL)
94c2aa98e2SPeter Wemm 	{
95c2aa98e2SPeter Wemm 		/* we don't support caching */
96c2aa98e2SPeter Wemm 		return;
97c2aa98e2SPeter Wemm 	}
98c2aa98e2SPeter Wemm 
99c2aa98e2SPeter Wemm 	if (mci->mci_host == NULL)
100c2aa98e2SPeter Wemm 		return;
101c2aa98e2SPeter Wemm 
102c2aa98e2SPeter Wemm 	/* if this is already cached, we are done */
103c2aa98e2SPeter Wemm 	if (bitset(MCIF_CACHED, mci->mci_flags))
104c2aa98e2SPeter Wemm 		return;
105c2aa98e2SPeter Wemm 
106c2aa98e2SPeter Wemm 	/* otherwise we may have to clear the slot */
107c2aa98e2SPeter Wemm 	if (*mcislot != NULL)
108c2aa98e2SPeter Wemm 		mci_uncache(mcislot, TRUE);
109c2aa98e2SPeter Wemm 
110c2aa98e2SPeter Wemm 	if (tTd(42, 5))
1113299c2f1SGregory Neil Shapiro 		dprintf("mci_cache: caching %lx (%s) in slot %d\n",
1123299c2f1SGregory Neil Shapiro 			(u_long) mci, mci->mci_host,
1133299c2f1SGregory Neil Shapiro 			(int)(mcislot - MciCache));
114c2aa98e2SPeter Wemm 	if (tTd(91, 100))
115c2aa98e2SPeter Wemm 		sm_syslog(LOG_DEBUG, CurEnv->e_id,
1163299c2f1SGregory Neil Shapiro 			  "mci_cache: caching %lx (%.100s) in slot %d",
1173299c2f1SGregory Neil Shapiro 			  (u_long) mci, mci->mci_host, mcislot - MciCache);
118c2aa98e2SPeter Wemm 
119c2aa98e2SPeter Wemm 	*mcislot = mci;
120c2aa98e2SPeter Wemm 	mci->mci_flags |= MCIF_CACHED;
121c2aa98e2SPeter Wemm }
122c2aa98e2SPeter Wemm /*
123c2aa98e2SPeter Wemm **  MCI_SCAN -- scan the cache, flush junk, and return best slot
124c2aa98e2SPeter Wemm **
125c2aa98e2SPeter Wemm **	Parameters:
126c2aa98e2SPeter Wemm **		savemci -- never flush this one.  Can be null.
127c2aa98e2SPeter Wemm **
128c2aa98e2SPeter Wemm **	Returns:
129c2aa98e2SPeter Wemm **		The LRU (or empty) slot.
130c2aa98e2SPeter Wemm */
131c2aa98e2SPeter Wemm 
132c2aa98e2SPeter Wemm MCI **
133c2aa98e2SPeter Wemm mci_scan(savemci)
134c2aa98e2SPeter Wemm 	MCI *savemci;
135c2aa98e2SPeter Wemm {
136c2aa98e2SPeter Wemm 	time_t now;
137c2aa98e2SPeter Wemm 	register MCI **bestmci;
138c2aa98e2SPeter Wemm 	register MCI *mci;
139c2aa98e2SPeter Wemm 	register int i;
140c2aa98e2SPeter Wemm 
141c2aa98e2SPeter Wemm 	if (MaxMciCache <= 0)
142c2aa98e2SPeter Wemm 	{
143c2aa98e2SPeter Wemm 		/* we don't support caching */
144c2aa98e2SPeter Wemm 		return NULL;
145c2aa98e2SPeter Wemm 	}
146c2aa98e2SPeter Wemm 
147c2aa98e2SPeter Wemm 	if (MciCache == NULL)
148c2aa98e2SPeter Wemm 	{
149c2aa98e2SPeter Wemm 		/* first call */
150c2aa98e2SPeter Wemm 		MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache);
1513299c2f1SGregory Neil Shapiro 		memset((char *) MciCache, '\0', MaxMciCache * sizeof *MciCache);
1523299c2f1SGregory Neil Shapiro 		return &MciCache[0];
153c2aa98e2SPeter Wemm 	}
154c2aa98e2SPeter Wemm 
155c2aa98e2SPeter Wemm 	now = curtime();
156c2aa98e2SPeter Wemm 	bestmci = &MciCache[0];
157c2aa98e2SPeter Wemm 	for (i = 0; i < MaxMciCache; i++)
158c2aa98e2SPeter Wemm 	{
159c2aa98e2SPeter Wemm 		mci = MciCache[i];
160c2aa98e2SPeter Wemm 		if (mci == NULL || mci->mci_state == MCIS_CLOSED)
161c2aa98e2SPeter Wemm 		{
162c2aa98e2SPeter Wemm 			bestmci = &MciCache[i];
163c2aa98e2SPeter Wemm 			continue;
164c2aa98e2SPeter Wemm 		}
1653299c2f1SGregory Neil Shapiro 		if ((mci->mci_lastuse + MciCacheTimeout < now ||
1663299c2f1SGregory Neil Shapiro 		     (mci->mci_mailer != NULL &&
1673299c2f1SGregory Neil Shapiro 		      mci->mci_mailer->m_maxdeliveries > 0 &&
1683299c2f1SGregory Neil Shapiro 		      mci->mci_deliveries + 1 >= mci->mci_mailer->m_maxdeliveries))&&
1693299c2f1SGregory Neil Shapiro 		    mci != savemci)
170c2aa98e2SPeter Wemm 		{
1713299c2f1SGregory Neil Shapiro 			/* connection idle too long or too many deliveries */
172c2aa98e2SPeter Wemm 			bestmci = &MciCache[i];
1733299c2f1SGregory Neil Shapiro 
1743299c2f1SGregory Neil Shapiro 			/* close it */
175c2aa98e2SPeter Wemm 			mci_uncache(bestmci, TRUE);
176c2aa98e2SPeter Wemm 			continue;
177c2aa98e2SPeter Wemm 		}
178c2aa98e2SPeter Wemm 		if (*bestmci == NULL)
179c2aa98e2SPeter Wemm 			continue;
180c2aa98e2SPeter Wemm 		if (mci->mci_lastuse < (*bestmci)->mci_lastuse)
181c2aa98e2SPeter Wemm 			bestmci = &MciCache[i];
182c2aa98e2SPeter Wemm 	}
183c2aa98e2SPeter Wemm 	return bestmci;
184c2aa98e2SPeter Wemm }
185c2aa98e2SPeter Wemm /*
186c2aa98e2SPeter Wemm **  MCI_UNCACHE -- remove a connection from a slot.
187c2aa98e2SPeter Wemm **
188c2aa98e2SPeter Wemm **	May close a connection.
189c2aa98e2SPeter Wemm **
190c2aa98e2SPeter Wemm **	Parameters:
191c2aa98e2SPeter Wemm **		mcislot -- the slot to empty.
192c2aa98e2SPeter Wemm **		doquit -- if TRUE, send QUIT protocol on this connection.
193c2aa98e2SPeter Wemm **			  if FALSE, we are assumed to be in a forked child;
194c2aa98e2SPeter Wemm **				all we want to do is close the file(s).
195c2aa98e2SPeter Wemm **
196c2aa98e2SPeter Wemm **	Returns:
197c2aa98e2SPeter Wemm **		none.
198c2aa98e2SPeter Wemm */
199c2aa98e2SPeter Wemm 
2003299c2f1SGregory Neil Shapiro static void
201c2aa98e2SPeter Wemm mci_uncache(mcislot, doquit)
202c2aa98e2SPeter Wemm 	register MCI **mcislot;
203c2aa98e2SPeter Wemm 	bool doquit;
204c2aa98e2SPeter Wemm {
205c2aa98e2SPeter Wemm 	register MCI *mci;
206c2aa98e2SPeter Wemm 	extern ENVELOPE BlankEnvelope;
207c2aa98e2SPeter Wemm 
208c2aa98e2SPeter Wemm 	mci = *mcislot;
209c2aa98e2SPeter Wemm 	if (mci == NULL)
210c2aa98e2SPeter Wemm 		return;
211c2aa98e2SPeter Wemm 	*mcislot = NULL;
212c2aa98e2SPeter Wemm 	if (mci->mci_host == NULL)
213c2aa98e2SPeter Wemm 		return;
214c2aa98e2SPeter Wemm 
215c2aa98e2SPeter Wemm 	mci_unlock_host(mci);
216c2aa98e2SPeter Wemm 
217c2aa98e2SPeter Wemm 	if (tTd(42, 5))
2183299c2f1SGregory Neil Shapiro 		dprintf("mci_uncache: uncaching %lx (%s) from slot %d (%d)\n",
219c2aa98e2SPeter Wemm 			(u_long) mci, mci->mci_host,
220c2aa98e2SPeter Wemm 			(int)(mcislot - MciCache), doquit);
221c2aa98e2SPeter Wemm 	if (tTd(91, 100))
222c2aa98e2SPeter Wemm 		sm_syslog(LOG_DEBUG, CurEnv->e_id,
2233299c2f1SGregory Neil Shapiro 			  "mci_uncache: uncaching %lx (%.100s) from slot %d (%d)",
2243299c2f1SGregory Neil Shapiro 			  (u_long) mci, mci->mci_host,
2253299c2f1SGregory Neil Shapiro 			  mcislot - MciCache, doquit);
226c2aa98e2SPeter Wemm 
2273299c2f1SGregory Neil Shapiro 	mci->mci_deliveries = 0;
228c2aa98e2SPeter Wemm #if SMTP
229c2aa98e2SPeter Wemm 	if (doquit)
230c2aa98e2SPeter Wemm 	{
231c2aa98e2SPeter Wemm 		message("Closing connection to %s", mci->mci_host);
232c2aa98e2SPeter Wemm 
233c2aa98e2SPeter Wemm 		mci->mci_flags &= ~MCIF_CACHED;
234c2aa98e2SPeter Wemm 
235c2aa98e2SPeter Wemm 		/* only uses the envelope to flush the transcript file */
236c2aa98e2SPeter Wemm 		if (mci->mci_state != MCIS_CLOSED)
237c2aa98e2SPeter Wemm 			smtpquit(mci->mci_mailer, mci, &BlankEnvelope);
238c2aa98e2SPeter Wemm # ifdef XLA
239c2aa98e2SPeter Wemm 		xla_host_end(mci->mci_host);
2403299c2f1SGregory Neil Shapiro # endif /* XLA */
241c2aa98e2SPeter Wemm 	}
242c2aa98e2SPeter Wemm 	else
2433299c2f1SGregory Neil Shapiro #endif /* SMTP */
244c2aa98e2SPeter Wemm 	{
245c2aa98e2SPeter Wemm 		if (mci->mci_in != NULL)
2463299c2f1SGregory Neil Shapiro 			(void) fclose(mci->mci_in);
247c2aa98e2SPeter Wemm 		if (mci->mci_out != NULL)
2483299c2f1SGregory Neil Shapiro 			(void) fclose(mci->mci_out);
249c2aa98e2SPeter Wemm 		mci->mci_in = mci->mci_out = NULL;
250c2aa98e2SPeter Wemm 		mci->mci_state = MCIS_CLOSED;
251c2aa98e2SPeter Wemm 		mci->mci_exitstat = EX_OK;
252c2aa98e2SPeter Wemm 		mci->mci_errno = 0;
253c2aa98e2SPeter Wemm 		mci->mci_flags = 0;
254c2aa98e2SPeter Wemm 	}
255c2aa98e2SPeter Wemm }
256c2aa98e2SPeter Wemm /*
257c2aa98e2SPeter Wemm **  MCI_FLUSH -- flush the entire cache
258c2aa98e2SPeter Wemm **
259c2aa98e2SPeter Wemm **	Parameters:
260c2aa98e2SPeter Wemm **		doquit -- if TRUE, send QUIT protocol.
261c2aa98e2SPeter Wemm **			  if FALSE, just close the connection.
262c2aa98e2SPeter Wemm **		allbut -- but leave this one open.
263c2aa98e2SPeter Wemm **
264c2aa98e2SPeter Wemm **	Returns:
265c2aa98e2SPeter Wemm **		none.
266c2aa98e2SPeter Wemm */
267c2aa98e2SPeter Wemm 
268c2aa98e2SPeter Wemm void
269c2aa98e2SPeter Wemm mci_flush(doquit, allbut)
270c2aa98e2SPeter Wemm 	bool doquit;
271c2aa98e2SPeter Wemm 	MCI *allbut;
272c2aa98e2SPeter Wemm {
273c2aa98e2SPeter Wemm 	register int i;
274c2aa98e2SPeter Wemm 
275c2aa98e2SPeter Wemm 	if (MciCache == NULL)
276c2aa98e2SPeter Wemm 		return;
277c2aa98e2SPeter Wemm 
278c2aa98e2SPeter Wemm 	for (i = 0; i < MaxMciCache; i++)
279c46d91b7SGregory Neil Shapiro 	{
280c2aa98e2SPeter Wemm 		if (allbut != MciCache[i])
281c2aa98e2SPeter Wemm 			mci_uncache(&MciCache[i], doquit);
282c2aa98e2SPeter Wemm 	}
283c46d91b7SGregory Neil Shapiro }
284c2aa98e2SPeter Wemm /*
285c2aa98e2SPeter Wemm **  MCI_GET -- get information about a particular host
286c2aa98e2SPeter Wemm */
287c2aa98e2SPeter Wemm 
288c2aa98e2SPeter Wemm MCI *
289c2aa98e2SPeter Wemm mci_get(host, m)
290c2aa98e2SPeter Wemm 	char *host;
291c2aa98e2SPeter Wemm 	MAILER *m;
292c2aa98e2SPeter Wemm {
293c2aa98e2SPeter Wemm 	register MCI *mci;
294c2aa98e2SPeter Wemm 	register STAB *s;
295c2aa98e2SPeter Wemm 
296c2aa98e2SPeter Wemm #if DAEMON
297c2aa98e2SPeter Wemm 	extern SOCKADDR CurHostAddr;
298c2aa98e2SPeter Wemm 
299c2aa98e2SPeter Wemm 	/* clear CurHostAddr so we don't get a bogus address with this name */
3003299c2f1SGregory Neil Shapiro 	memset(&CurHostAddr, '\0', sizeof CurHostAddr);
3013299c2f1SGregory Neil Shapiro #endif /* DAEMON */
302c2aa98e2SPeter Wemm 
303c2aa98e2SPeter Wemm 	/* clear out any expired connections */
304c2aa98e2SPeter Wemm 	(void) mci_scan(NULL);
305c2aa98e2SPeter Wemm 
306c2aa98e2SPeter Wemm 	if (m->m_mno < 0)
307c46d91b7SGregory Neil Shapiro 		syserr("!negative mno %d (%s)", m->m_mno, m->m_name);
3083299c2f1SGregory Neil Shapiro 
309c2aa98e2SPeter Wemm 	s = stab(host, ST_MCI + m->m_mno, ST_ENTER);
310c2aa98e2SPeter Wemm 	mci = &s->s_mci;
311c2aa98e2SPeter Wemm 
3123299c2f1SGregory Neil Shapiro 	/*
3133299c2f1SGregory Neil Shapiro 	**  We don't need to load the peristent data if we have data
3143299c2f1SGregory Neil Shapiro 	**  already loaded in the cache.
3153299c2f1SGregory Neil Shapiro 	*/
3163299c2f1SGregory Neil Shapiro 
3173299c2f1SGregory Neil Shapiro 	if (mci->mci_host == NULL &&
3183299c2f1SGregory Neil Shapiro 	    (mci->mci_host = s->s_name) != NULL &&
3193299c2f1SGregory Neil Shapiro 	    !mci_load_persistent(mci))
320c2aa98e2SPeter Wemm 	{
321c2aa98e2SPeter Wemm 		if (tTd(42, 2))
3223299c2f1SGregory Neil Shapiro 			dprintf("mci_get(%s %s): lock failed\n",
3233299c2f1SGregory Neil Shapiro 				host, m->m_name);
324c2aa98e2SPeter Wemm 		mci->mci_exitstat = EX_TEMPFAIL;
325c2aa98e2SPeter Wemm 		mci->mci_state = MCIS_CLOSED;
326c2aa98e2SPeter Wemm 		mci->mci_statfile = NULL;
327c2aa98e2SPeter Wemm 		return mci;
328c2aa98e2SPeter Wemm 	}
329c2aa98e2SPeter Wemm 
330c2aa98e2SPeter Wemm 	if (tTd(42, 2))
331c2aa98e2SPeter Wemm 	{
3323299c2f1SGregory Neil Shapiro 		dprintf("mci_get(%s %s): mci_state=%d, _flags=%lx, _exitstat=%d, _errno=%d\n",
333c2aa98e2SPeter Wemm 			host, m->m_name, mci->mci_state, mci->mci_flags,
334c2aa98e2SPeter Wemm 			mci->mci_exitstat, mci->mci_errno);
335c2aa98e2SPeter Wemm 	}
336c2aa98e2SPeter Wemm 
337c2aa98e2SPeter Wemm #if SMTP
338c2aa98e2SPeter Wemm 	if (mci->mci_state == MCIS_OPEN)
339c2aa98e2SPeter Wemm 	{
340c2aa98e2SPeter Wemm 		/* poke the connection to see if it's still alive */
341c2aa98e2SPeter Wemm 		(void) smtpprobe(mci);
342c2aa98e2SPeter Wemm 
343c2aa98e2SPeter Wemm 		/* reset the stored state in the event of a timeout */
344c2aa98e2SPeter Wemm 		if (mci->mci_state != MCIS_OPEN)
345c2aa98e2SPeter Wemm 		{
346c2aa98e2SPeter Wemm 			mci->mci_errno = 0;
347c2aa98e2SPeter Wemm 			mci->mci_exitstat = EX_OK;
348c2aa98e2SPeter Wemm 			mci->mci_state = MCIS_CLOSED;
349c2aa98e2SPeter Wemm 		}
350c2aa98e2SPeter Wemm # if DAEMON
351c2aa98e2SPeter Wemm 		else
352c2aa98e2SPeter Wemm 		{
353c2aa98e2SPeter Wemm 			/* get peer host address for logging reasons only */
354c2aa98e2SPeter Wemm 			/* (this should really be in the mci struct) */
355c2aa98e2SPeter Wemm 			SOCKADDR_LEN_T socklen = sizeof CurHostAddr;
356c2aa98e2SPeter Wemm 
357c2aa98e2SPeter Wemm 			(void) getpeername(fileno(mci->mci_in),
358c2aa98e2SPeter Wemm 				(struct sockaddr *) &CurHostAddr, &socklen);
359c2aa98e2SPeter Wemm 		}
3603299c2f1SGregory Neil Shapiro # endif /* DAEMON */
361c2aa98e2SPeter Wemm 	}
3623299c2f1SGregory Neil Shapiro #endif /* SMTP */
363c2aa98e2SPeter Wemm 	if (mci->mci_state == MCIS_CLOSED)
364c2aa98e2SPeter Wemm 	{
365c2aa98e2SPeter Wemm 		time_t now = curtime();
366c2aa98e2SPeter Wemm 
367c2aa98e2SPeter Wemm 		/* if this info is stale, ignore it */
368c2aa98e2SPeter Wemm 		if (now > mci->mci_lastuse + MciInfoTimeout)
369c2aa98e2SPeter Wemm 		{
370c2aa98e2SPeter Wemm 			mci->mci_lastuse = now;
371c2aa98e2SPeter Wemm 			mci->mci_errno = 0;
372c2aa98e2SPeter Wemm 			mci->mci_exitstat = EX_OK;
373c2aa98e2SPeter Wemm 		}
374c2aa98e2SPeter Wemm 	}
375c2aa98e2SPeter Wemm 
376c2aa98e2SPeter Wemm 	return mci;
377c2aa98e2SPeter Wemm }
378c2aa98e2SPeter Wemm /*
3793299c2f1SGregory Neil Shapiro **  MCI_MATCH -- check connection cache for a particular host
3803299c2f1SGregory Neil Shapiro */
3813299c2f1SGregory Neil Shapiro 
3823299c2f1SGregory Neil Shapiro bool
3833299c2f1SGregory Neil Shapiro mci_match(host, m)
3843299c2f1SGregory Neil Shapiro 	char *host;
3853299c2f1SGregory Neil Shapiro 	MAILER *m;
3863299c2f1SGregory Neil Shapiro {
3873299c2f1SGregory Neil Shapiro 	register MCI *mci;
3883299c2f1SGregory Neil Shapiro 	register STAB *s;
3893299c2f1SGregory Neil Shapiro 
390c46d91b7SGregory Neil Shapiro 	if (m->m_mno < 0 || m->m_mno > MAXMAILERS)
3913299c2f1SGregory Neil Shapiro 		return FALSE;
3923299c2f1SGregory Neil Shapiro 	s = stab(host, ST_MCI + m->m_mno, ST_FIND);
3933299c2f1SGregory Neil Shapiro 	if (s == NULL)
3943299c2f1SGregory Neil Shapiro 		return FALSE;
3953299c2f1SGregory Neil Shapiro 
3963299c2f1SGregory Neil Shapiro 	mci = &s->s_mci;
3973299c2f1SGregory Neil Shapiro 	if (mci->mci_state == MCIS_OPEN)
3983299c2f1SGregory Neil Shapiro 		return TRUE;
3993299c2f1SGregory Neil Shapiro 	return FALSE;
4003299c2f1SGregory Neil Shapiro }
4013299c2f1SGregory Neil Shapiro /*
402c2aa98e2SPeter Wemm **  MCI_SETSTAT -- set status codes in MCI structure.
403c2aa98e2SPeter Wemm **
404c2aa98e2SPeter Wemm **	Parameters:
405c2aa98e2SPeter Wemm **		mci -- the MCI structure to set.
406c2aa98e2SPeter Wemm **		xstat -- the exit status code.
407c2aa98e2SPeter Wemm **		dstat -- the DSN status code.
408c2aa98e2SPeter Wemm **		rstat -- the SMTP status code.
409c2aa98e2SPeter Wemm **
410c2aa98e2SPeter Wemm **	Returns:
411c2aa98e2SPeter Wemm **		none.
412c2aa98e2SPeter Wemm */
413c2aa98e2SPeter Wemm 
414c2aa98e2SPeter Wemm void
415c2aa98e2SPeter Wemm mci_setstat(mci, xstat, dstat, rstat)
416c2aa98e2SPeter Wemm 	MCI *mci;
417c2aa98e2SPeter Wemm 	int xstat;
418c2aa98e2SPeter Wemm 	char *dstat;
419c2aa98e2SPeter Wemm 	char *rstat;
420c2aa98e2SPeter Wemm {
421c2aa98e2SPeter Wemm 	/* protocol errors should never be interpreted as sticky */
422c2aa98e2SPeter Wemm 	if (xstat != EX_NOTSTICKY && xstat != EX_PROTOCOL)
423c2aa98e2SPeter Wemm 		mci->mci_exitstat = xstat;
424c2aa98e2SPeter Wemm 
425c2aa98e2SPeter Wemm 	mci->mci_status = dstat;
426c2aa98e2SPeter Wemm 	if (mci->mci_rstatus != NULL)
427c2aa98e2SPeter Wemm 		free(mci->mci_rstatus);
428c2aa98e2SPeter Wemm 	if (rstat != NULL)
429c2aa98e2SPeter Wemm 		rstat = newstr(rstat);
430c2aa98e2SPeter Wemm 	mci->mci_rstatus = rstat;
431c2aa98e2SPeter Wemm }
432c2aa98e2SPeter Wemm /*
433c2aa98e2SPeter Wemm **  MCI_DUMP -- dump the contents of an MCI structure.
434c2aa98e2SPeter Wemm **
435c2aa98e2SPeter Wemm **	Parameters:
436c2aa98e2SPeter Wemm **		mci -- the MCI structure to dump.
437c2aa98e2SPeter Wemm **
438c2aa98e2SPeter Wemm **	Returns:
439c2aa98e2SPeter Wemm **		none.
440c2aa98e2SPeter Wemm **
441c2aa98e2SPeter Wemm **	Side Effects:
442c2aa98e2SPeter Wemm **		none.
443c2aa98e2SPeter Wemm */
444c2aa98e2SPeter Wemm 
445c2aa98e2SPeter Wemm struct mcifbits
446c2aa98e2SPeter Wemm {
447c2aa98e2SPeter Wemm 	int	mcif_bit;	/* flag bit */
448c2aa98e2SPeter Wemm 	char	*mcif_name;	/* flag name */
449c2aa98e2SPeter Wemm };
4503299c2f1SGregory Neil Shapiro static struct mcifbits	MciFlags[] =
451c2aa98e2SPeter Wemm {
452c2aa98e2SPeter Wemm 	{ MCIF_VALID,		"VALID"		},
453c2aa98e2SPeter Wemm 	{ MCIF_TEMP,		"TEMP"		},
454c2aa98e2SPeter Wemm 	{ MCIF_CACHED,		"CACHED"	},
455c2aa98e2SPeter Wemm 	{ MCIF_ESMTP,		"ESMTP"		},
456c2aa98e2SPeter Wemm 	{ MCIF_EXPN,		"EXPN"		},
457c2aa98e2SPeter Wemm 	{ MCIF_SIZE,		"SIZE"		},
458c2aa98e2SPeter Wemm 	{ MCIF_8BITMIME,	"8BITMIME"	},
459c2aa98e2SPeter Wemm 	{ MCIF_7BIT,		"7BIT"		},
460c2aa98e2SPeter Wemm 	{ MCIF_MULTSTAT,	"MULTSTAT"	},
461c2aa98e2SPeter Wemm 	{ MCIF_INHEADER,	"INHEADER"	},
462c2aa98e2SPeter Wemm 	{ MCIF_CVT8TO7,		"CVT8TO7"	},
463c2aa98e2SPeter Wemm 	{ MCIF_DSN,		"DSN"		},
464c2aa98e2SPeter Wemm 	{ MCIF_8BITOK,		"8BITOK"	},
465c2aa98e2SPeter Wemm 	{ MCIF_CVT7TO8,		"CVT7TO8"	},
466c2aa98e2SPeter Wemm 	{ MCIF_INMIME,		"INMIME"	},
467c2aa98e2SPeter Wemm 	{ 0,			NULL		}
468c2aa98e2SPeter Wemm };
469c2aa98e2SPeter Wemm 
470c2aa98e2SPeter Wemm 
471c2aa98e2SPeter Wemm void
472c2aa98e2SPeter Wemm mci_dump(mci, logit)
473c2aa98e2SPeter Wemm 	register MCI *mci;
474c2aa98e2SPeter Wemm 	bool logit;
475c2aa98e2SPeter Wemm {
476c2aa98e2SPeter Wemm 	register char *p;
477c2aa98e2SPeter Wemm 	char *sep;
478c2aa98e2SPeter Wemm 	char buf[4000];
479c2aa98e2SPeter Wemm 
480c2aa98e2SPeter Wemm 	sep = logit ? " " : "\n\t";
481c2aa98e2SPeter Wemm 	p = buf;
482b7db722fSBruce Evans 	snprintf(p, SPACELEFT(buf, p), "MCI@%lx: ",
483b7db722fSBruce Evans 		sizeof(void *) == sizeof(u_long) ?
484b7db722fSBruce Evans 		(u_long)(void *)mci : (u_long)(u_int)(void *)mci);
485c2aa98e2SPeter Wemm 	p += strlen(p);
486c2aa98e2SPeter Wemm 	if (mci == NULL)
487c2aa98e2SPeter Wemm 	{
488c2aa98e2SPeter Wemm 		snprintf(p, SPACELEFT(buf, p), "NULL");
489c2aa98e2SPeter Wemm 		goto printit;
490c2aa98e2SPeter Wemm 	}
4913299c2f1SGregory Neil Shapiro 	snprintf(p, SPACELEFT(buf, p), "flags=%lx", mci->mci_flags);
492c2aa98e2SPeter Wemm 	p += strlen(p);
493c2aa98e2SPeter Wemm 	if (mci->mci_flags != 0)
494c2aa98e2SPeter Wemm 	{
495c2aa98e2SPeter Wemm 		struct mcifbits *f;
496c2aa98e2SPeter Wemm 
497c2aa98e2SPeter Wemm 		*p++ = '<';
498c2aa98e2SPeter Wemm 		for (f = MciFlags; f->mcif_bit != 0; f++)
499c2aa98e2SPeter Wemm 		{
500c2aa98e2SPeter Wemm 			if (!bitset(f->mcif_bit, mci->mci_flags))
501c2aa98e2SPeter Wemm 				continue;
502c2aa98e2SPeter Wemm 			snprintf(p, SPACELEFT(buf, p), "%s,", f->mcif_name);
503c2aa98e2SPeter Wemm 			p += strlen(p);
504c2aa98e2SPeter Wemm 		}
505c2aa98e2SPeter Wemm 		p[-1] = '>';
506c2aa98e2SPeter Wemm 	}
507c2aa98e2SPeter Wemm 	snprintf(p, SPACELEFT(buf, p),
508c2aa98e2SPeter Wemm 		",%serrno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s",
509c2aa98e2SPeter Wemm 		sep, mci->mci_errno, mci->mci_herrno,
5103299c2f1SGregory Neil Shapiro 		mci->mci_exitstat, mci->mci_state, (int) mci->mci_pid, sep);
511c2aa98e2SPeter Wemm 	p += strlen(p);
512c2aa98e2SPeter Wemm 	snprintf(p, SPACELEFT(buf, p),
513c2aa98e2SPeter Wemm 		"maxsize=%ld, phase=%s, mailer=%s,%s",
514c2aa98e2SPeter Wemm 		mci->mci_maxsize,
515c2aa98e2SPeter Wemm 		mci->mci_phase == NULL ? "NULL" : mci->mci_phase,
516c2aa98e2SPeter Wemm 		mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name,
517c2aa98e2SPeter Wemm 		sep);
518c2aa98e2SPeter Wemm 	p += strlen(p);
519c2aa98e2SPeter Wemm 	snprintf(p, SPACELEFT(buf, p),
520c2aa98e2SPeter Wemm 		"status=%s, rstatus=%s,%s",
521c2aa98e2SPeter Wemm 		mci->mci_status == NULL ? "NULL" : mci->mci_status,
522c2aa98e2SPeter Wemm 		mci->mci_rstatus == NULL ? "NULL" : mci->mci_rstatus,
523c2aa98e2SPeter Wemm 		sep);
524c2aa98e2SPeter Wemm 	p += strlen(p);
525c2aa98e2SPeter Wemm 	snprintf(p, SPACELEFT(buf, p),
526c2aa98e2SPeter Wemm 		"host=%s, lastuse=%s",
527c2aa98e2SPeter Wemm 		mci->mci_host == NULL ? "NULL" : mci->mci_host,
528c2aa98e2SPeter Wemm 		ctime(&mci->mci_lastuse));
529c2aa98e2SPeter Wemm printit:
530c2aa98e2SPeter Wemm 	if (logit)
531c2aa98e2SPeter Wemm 		sm_syslog(LOG_DEBUG, CurEnv->e_id, "%.1000s", buf);
532c2aa98e2SPeter Wemm 	else
533c2aa98e2SPeter Wemm 		printf("%s\n", buf);
534c2aa98e2SPeter Wemm }
535c2aa98e2SPeter Wemm /*
536c2aa98e2SPeter Wemm **  MCI_DUMP_ALL -- print the entire MCI cache
537c2aa98e2SPeter Wemm **
538c2aa98e2SPeter Wemm **	Parameters:
539c2aa98e2SPeter Wemm **		logit -- if set, log the result instead of printing
540c2aa98e2SPeter Wemm **			to stdout.
541c2aa98e2SPeter Wemm **
542c2aa98e2SPeter Wemm **	Returns:
543c2aa98e2SPeter Wemm **		none.
544c2aa98e2SPeter Wemm */
545c2aa98e2SPeter Wemm 
546c2aa98e2SPeter Wemm void
547c2aa98e2SPeter Wemm mci_dump_all(logit)
548c2aa98e2SPeter Wemm 	bool logit;
549c2aa98e2SPeter Wemm {
550c2aa98e2SPeter Wemm 	register int i;
551c2aa98e2SPeter Wemm 
552c2aa98e2SPeter Wemm 	if (MciCache == NULL)
553c2aa98e2SPeter Wemm 		return;
554c2aa98e2SPeter Wemm 
555c2aa98e2SPeter Wemm 	for (i = 0; i < MaxMciCache; i++)
556c2aa98e2SPeter Wemm 		mci_dump(MciCache[i], logit);
557c2aa98e2SPeter Wemm }
558c2aa98e2SPeter Wemm /*
559c2aa98e2SPeter Wemm **  MCI_LOCK_HOST -- Lock host while sending.
560c2aa98e2SPeter Wemm **
561c2aa98e2SPeter Wemm **	If we are contacting a host, we'll need to
562c2aa98e2SPeter Wemm **	update the status information in the host status
563c2aa98e2SPeter Wemm **	file, and if we want to do that, we ought to have
564c2aa98e2SPeter Wemm **	locked it. This has the (according to some)
565c2aa98e2SPeter Wemm **	desirable effect of serializing connectivity with
566c2aa98e2SPeter Wemm **	remote hosts -- i.e.: one connection to a give
567c2aa98e2SPeter Wemm **	host at a time.
568c2aa98e2SPeter Wemm **
569c2aa98e2SPeter Wemm **	Parameters:
570c2aa98e2SPeter Wemm **		mci -- containing the host we want to lock.
571c2aa98e2SPeter Wemm **
572c2aa98e2SPeter Wemm **	Returns:
573c2aa98e2SPeter Wemm **		EX_OK	    -- got the lock.
574c2aa98e2SPeter Wemm **		EX_TEMPFAIL -- didn't get the lock.
575c2aa98e2SPeter Wemm */
576c2aa98e2SPeter Wemm 
577c2aa98e2SPeter Wemm int
578c2aa98e2SPeter Wemm mci_lock_host(mci)
579c2aa98e2SPeter Wemm 	MCI *mci;
580c2aa98e2SPeter Wemm {
581c2aa98e2SPeter Wemm 	if (mci == NULL)
582c2aa98e2SPeter Wemm 	{
583c2aa98e2SPeter Wemm 		if (tTd(56, 1))
5843299c2f1SGregory Neil Shapiro 			dprintf("mci_lock_host: NULL mci\n");
585c2aa98e2SPeter Wemm 		return EX_OK;
586c2aa98e2SPeter Wemm 	}
587c2aa98e2SPeter Wemm 
588c2aa98e2SPeter Wemm 	if (!SingleThreadDelivery)
589c2aa98e2SPeter Wemm 		return EX_OK;
590c2aa98e2SPeter Wemm 
591c2aa98e2SPeter Wemm 	return mci_lock_host_statfile(mci);
592c2aa98e2SPeter Wemm }
593c2aa98e2SPeter Wemm 
5943299c2f1SGregory Neil Shapiro static int
595c2aa98e2SPeter Wemm mci_lock_host_statfile(mci)
596c2aa98e2SPeter Wemm 	MCI *mci;
597c2aa98e2SPeter Wemm {
5983299c2f1SGregory Neil Shapiro 	int save_errno = errno;
599c2aa98e2SPeter Wemm 	int retVal = EX_OK;
600c2aa98e2SPeter Wemm 	char fname[MAXPATHLEN + 1];
601c2aa98e2SPeter Wemm 
602c2aa98e2SPeter Wemm 	if (HostStatDir == NULL || mci->mci_host == NULL)
603c2aa98e2SPeter Wemm 		return EX_OK;
604c2aa98e2SPeter Wemm 
605c2aa98e2SPeter Wemm 	if (tTd(56, 2))
6063299c2f1SGregory Neil Shapiro 		dprintf("mci_lock_host: attempting to lock %s\n",
607c2aa98e2SPeter Wemm 			mci->mci_host);
608c2aa98e2SPeter Wemm 
609c2aa98e2SPeter Wemm 	if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname, TRUE) < 0)
610c2aa98e2SPeter Wemm 	{
611c2aa98e2SPeter Wemm 		/* of course this should never happen */
612c2aa98e2SPeter Wemm 		if (tTd(56, 2))
6133299c2f1SGregory Neil Shapiro 			dprintf("mci_lock_host: Failed to generate host path for %s\n",
614c2aa98e2SPeter Wemm 				mci->mci_host);
615c2aa98e2SPeter Wemm 
616c2aa98e2SPeter Wemm 		retVal = EX_TEMPFAIL;
617c2aa98e2SPeter Wemm 		goto cleanup;
618c2aa98e2SPeter Wemm 	}
619c2aa98e2SPeter Wemm 
620c2aa98e2SPeter Wemm 	mci->mci_statfile = safefopen(fname, O_RDWR, FileMode,
621c2aa98e2SPeter Wemm 				      SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH|SFF_CREAT);
622c2aa98e2SPeter Wemm 
623c2aa98e2SPeter Wemm 	if (mci->mci_statfile == NULL)
624c2aa98e2SPeter Wemm 	{
625c2aa98e2SPeter Wemm 		syserr("mci_lock_host: cannot create host lock file %s",
626c2aa98e2SPeter Wemm 			       fname);
627c2aa98e2SPeter Wemm 		goto cleanup;
628c2aa98e2SPeter Wemm 	}
629c2aa98e2SPeter Wemm 
630c2aa98e2SPeter Wemm 	if (!lockfile(fileno(mci->mci_statfile), fname, "", LOCK_EX|LOCK_NB))
631c2aa98e2SPeter Wemm 	{
632c2aa98e2SPeter Wemm 		if (tTd(56, 2))
6333299c2f1SGregory Neil Shapiro 			dprintf("mci_lock_host: couldn't get lock on %s\n",
634c2aa98e2SPeter Wemm 				fname);
6353299c2f1SGregory Neil Shapiro 		(void) fclose(mci->mci_statfile);
636c2aa98e2SPeter Wemm 		mci->mci_statfile = NULL;
637c2aa98e2SPeter Wemm 		retVal = EX_TEMPFAIL;
638c2aa98e2SPeter Wemm 		goto cleanup;
639c2aa98e2SPeter Wemm 	}
640c2aa98e2SPeter Wemm 
641c2aa98e2SPeter Wemm 	if (tTd(56, 12) && mci->mci_statfile != NULL)
6423299c2f1SGregory Neil Shapiro 		dprintf("mci_lock_host: Sanity check -- lock is good\n");
643c2aa98e2SPeter Wemm 
644c2aa98e2SPeter Wemm cleanup:
6453299c2f1SGregory Neil Shapiro 	errno = save_errno;
646c2aa98e2SPeter Wemm 	return retVal;
647c2aa98e2SPeter Wemm }
648c2aa98e2SPeter Wemm /*
649c2aa98e2SPeter Wemm **  MCI_UNLOCK_HOST -- unlock host
650c2aa98e2SPeter Wemm **
651c2aa98e2SPeter Wemm **	Clean up the lock on a host, close the file, let
652c2aa98e2SPeter Wemm **	someone else use it.
653c2aa98e2SPeter Wemm **
654c2aa98e2SPeter Wemm **	Parameters:
655c2aa98e2SPeter Wemm **		mci -- us.
656c2aa98e2SPeter Wemm **
657c2aa98e2SPeter Wemm **	Returns:
658c2aa98e2SPeter Wemm **		nothing.
659c2aa98e2SPeter Wemm */
660c2aa98e2SPeter Wemm 
661c2aa98e2SPeter Wemm void
662c2aa98e2SPeter Wemm mci_unlock_host(mci)
663c2aa98e2SPeter Wemm 	MCI *mci;
664c2aa98e2SPeter Wemm {
6653299c2f1SGregory Neil Shapiro 	int save_errno = errno;
666c2aa98e2SPeter Wemm 
667c2aa98e2SPeter Wemm 	if (mci == NULL)
668c2aa98e2SPeter Wemm 	{
669c2aa98e2SPeter Wemm 		if (tTd(56, 1))
6703299c2f1SGregory Neil Shapiro 			dprintf("mci_unlock_host: NULL mci\n");
671c2aa98e2SPeter Wemm 		return;
672c2aa98e2SPeter Wemm 	}
673c2aa98e2SPeter Wemm 
674c2aa98e2SPeter Wemm 	if (HostStatDir == NULL || mci->mci_host == NULL)
675c2aa98e2SPeter Wemm 		return;
676c2aa98e2SPeter Wemm 
677c2aa98e2SPeter Wemm 	if (!SingleThreadDelivery && mci_lock_host_statfile(mci) == EX_TEMPFAIL)
678c2aa98e2SPeter Wemm 	{
679c2aa98e2SPeter Wemm 		if (tTd(56, 1))
6803299c2f1SGregory Neil Shapiro 			dprintf("mci_unlock_host: stat file already locked\n");
681c2aa98e2SPeter Wemm 	}
682c2aa98e2SPeter Wemm 	else
683c2aa98e2SPeter Wemm 	{
684c2aa98e2SPeter Wemm 		if (tTd(56, 2))
6853299c2f1SGregory Neil Shapiro 			dprintf("mci_unlock_host: store prior to unlock\n");
686c2aa98e2SPeter Wemm 
687c2aa98e2SPeter Wemm 		mci_store_persistent(mci);
688c2aa98e2SPeter Wemm 	}
689c2aa98e2SPeter Wemm 
690c2aa98e2SPeter Wemm 	if (mci->mci_statfile != NULL)
691c2aa98e2SPeter Wemm 	{
6923299c2f1SGregory Neil Shapiro 		(void) fclose(mci->mci_statfile);
693c2aa98e2SPeter Wemm 		mci->mci_statfile = NULL;
694c2aa98e2SPeter Wemm 	}
695c2aa98e2SPeter Wemm 
6963299c2f1SGregory Neil Shapiro 	errno = save_errno;
697c2aa98e2SPeter Wemm }
698c2aa98e2SPeter Wemm /*
699c2aa98e2SPeter Wemm **  MCI_LOAD_PERSISTENT -- load persistent host info
700c2aa98e2SPeter Wemm **
701c2aa98e2SPeter Wemm **	Load information about host that is kept
702c2aa98e2SPeter Wemm **	in common for all running sendmails.
703c2aa98e2SPeter Wemm **
704c2aa98e2SPeter Wemm **	Parameters:
705c2aa98e2SPeter Wemm **		mci -- the host/connection to load persistent info
706c2aa98e2SPeter Wemm **			   for.
707c2aa98e2SPeter Wemm **
708c2aa98e2SPeter Wemm **	Returns:
709c2aa98e2SPeter Wemm **		TRUE -- lock was successful
710c2aa98e2SPeter Wemm **		FALSE -- lock failed
711c2aa98e2SPeter Wemm */
712c2aa98e2SPeter Wemm 
7133299c2f1SGregory Neil Shapiro static bool
714c2aa98e2SPeter Wemm mci_load_persistent(mci)
715c2aa98e2SPeter Wemm 	MCI *mci;
716c2aa98e2SPeter Wemm {
7173299c2f1SGregory Neil Shapiro 	int save_errno = errno;
718c2aa98e2SPeter Wemm 	bool locked = TRUE;
719c2aa98e2SPeter Wemm 	FILE *fp;
720c2aa98e2SPeter Wemm 	char fname[MAXPATHLEN + 1];
721c2aa98e2SPeter Wemm 
722c2aa98e2SPeter Wemm 	if (mci == NULL)
723c2aa98e2SPeter Wemm 	{
724c2aa98e2SPeter Wemm 		if (tTd(56, 1))
7253299c2f1SGregory Neil Shapiro 			dprintf("mci_load_persistent: NULL mci\n");
726c2aa98e2SPeter Wemm 		return TRUE;
727c2aa98e2SPeter Wemm 	}
728c2aa98e2SPeter Wemm 
729c2aa98e2SPeter Wemm 	if (IgnoreHostStatus || HostStatDir == NULL || mci->mci_host == NULL)
730c2aa98e2SPeter Wemm 		return TRUE;
731c2aa98e2SPeter Wemm 
732c2aa98e2SPeter Wemm 	/* Already have the persistent information in memory */
733c2aa98e2SPeter Wemm 	if (SingleThreadDelivery && mci->mci_statfile != NULL)
734c2aa98e2SPeter Wemm 		return TRUE;
735c2aa98e2SPeter Wemm 
736c2aa98e2SPeter Wemm 	if (tTd(56, 1))
7373299c2f1SGregory Neil Shapiro 		dprintf("mci_load_persistent: Attempting to load persistent information for %s\n",
738c2aa98e2SPeter Wemm 			mci->mci_host);
739c2aa98e2SPeter Wemm 
740c2aa98e2SPeter Wemm 	if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname, FALSE) < 0)
741c2aa98e2SPeter Wemm 	{
742c2aa98e2SPeter Wemm 		/* Not much we can do if the file isn't there... */
743c2aa98e2SPeter Wemm 		if (tTd(56, 1))
7443299c2f1SGregory Neil Shapiro 			dprintf("mci_load_persistent: Couldn't generate host path\n");
745c2aa98e2SPeter Wemm 		goto cleanup;
746c2aa98e2SPeter Wemm 	}
747c2aa98e2SPeter Wemm 
748c2aa98e2SPeter Wemm 	fp = safefopen(fname, O_RDONLY, FileMode,
749c2aa98e2SPeter Wemm 		       SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH);
750c2aa98e2SPeter Wemm 	if (fp == NULL)
751c2aa98e2SPeter Wemm 	{
752c2aa98e2SPeter Wemm 		/* I can't think of any reason this should ever happen */
753c2aa98e2SPeter Wemm 		if (tTd(56, 1))
7543299c2f1SGregory Neil Shapiro 			dprintf("mci_load_persistent: open(%s): %s\n",
755c2aa98e2SPeter Wemm 				fname, errstring(errno));
756c2aa98e2SPeter Wemm 		goto cleanup;
757c2aa98e2SPeter Wemm 	}
758c2aa98e2SPeter Wemm 
759c2aa98e2SPeter Wemm 	FileName = fname;
760c2aa98e2SPeter Wemm 	locked = lockfile(fileno(fp), fname, "", LOCK_SH|LOCK_NB);
761c2aa98e2SPeter Wemm 	if (locked)
7623299c2f1SGregory Neil Shapiro 	{
7633299c2f1SGregory Neil Shapiro 		(void) mci_read_persistent(fp, mci);
7643299c2f1SGregory Neil Shapiro 		(void) lockfile(fileno(fp), fname, "", LOCK_UN);
7653299c2f1SGregory Neil Shapiro 	}
7663299c2f1SGregory Neil Shapiro 	FileName = NULL;
7673299c2f1SGregory Neil Shapiro 	(void) fclose(fp);
768c2aa98e2SPeter Wemm 
769c2aa98e2SPeter Wemm cleanup:
7703299c2f1SGregory Neil Shapiro 	errno = save_errno;
771c2aa98e2SPeter Wemm 	return locked;
772c2aa98e2SPeter Wemm }
773c2aa98e2SPeter Wemm /*
774c2aa98e2SPeter Wemm **  MCI_READ_PERSISTENT -- read persistent host status file
775c2aa98e2SPeter Wemm **
776c2aa98e2SPeter Wemm **	Parameters:
777c2aa98e2SPeter Wemm **		fp -- the file pointer to read.
778c2aa98e2SPeter Wemm **		mci -- the pointer to fill in.
779c2aa98e2SPeter Wemm **
780c2aa98e2SPeter Wemm **	Returns:
781c2aa98e2SPeter Wemm **		-1 -- if the file was corrupt.
782c2aa98e2SPeter Wemm **		0 -- otherwise.
783c2aa98e2SPeter Wemm **
784c2aa98e2SPeter Wemm **	Warning:
785c2aa98e2SPeter Wemm **		This code makes the assumption that this data
786c2aa98e2SPeter Wemm **		will be read in an atomic fashion, and that the data
787c2aa98e2SPeter Wemm **		was written in an atomic fashion.  Any other functioning
788c2aa98e2SPeter Wemm **		may lead to some form of insanity.  This should be
789c2aa98e2SPeter Wemm **		perfectly safe due to underlying stdio buffering.
790c2aa98e2SPeter Wemm */
791c2aa98e2SPeter Wemm 
7923299c2f1SGregory Neil Shapiro static int
793c2aa98e2SPeter Wemm mci_read_persistent(fp, mci)
794c2aa98e2SPeter Wemm 	FILE *fp;
795c2aa98e2SPeter Wemm 	register MCI *mci;
796c2aa98e2SPeter Wemm {
797c2aa98e2SPeter Wemm 	int ver;
798c2aa98e2SPeter Wemm 	register char *p;
799c2aa98e2SPeter Wemm 	int saveLineNumber = LineNumber;
800c2aa98e2SPeter Wemm 	char buf[MAXLINE];
801c2aa98e2SPeter Wemm 
802c2aa98e2SPeter Wemm 	if (fp == NULL)
803c2aa98e2SPeter Wemm 		syserr("mci_read_persistent: NULL fp");
804c2aa98e2SPeter Wemm 	if (mci == NULL)
805c2aa98e2SPeter Wemm 		syserr("mci_read_persistent: NULL mci");
806c2aa98e2SPeter Wemm 	if (tTd(56, 93))
807c2aa98e2SPeter Wemm 	{
8083299c2f1SGregory Neil Shapiro 		dprintf("mci_read_persistent: fp=%lx, mci=", (u_long) fp);
809c2aa98e2SPeter Wemm 		mci_dump(mci, FALSE);
810c2aa98e2SPeter Wemm 	}
811c2aa98e2SPeter Wemm 
812c2aa98e2SPeter Wemm 	mci->mci_status = NULL;
813c2aa98e2SPeter Wemm 	if (mci->mci_rstatus != NULL)
814c2aa98e2SPeter Wemm 		free(mci->mci_rstatus);
815c2aa98e2SPeter Wemm 	mci->mci_rstatus = NULL;
816c2aa98e2SPeter Wemm 
817c2aa98e2SPeter Wemm 	rewind(fp);
818c2aa98e2SPeter Wemm 	ver = -1;
819c2aa98e2SPeter Wemm 	LineNumber = 0;
820c2aa98e2SPeter Wemm 	while (fgets(buf, sizeof buf, fp) != NULL)
821c2aa98e2SPeter Wemm 	{
822c2aa98e2SPeter Wemm 		LineNumber++;
823c2aa98e2SPeter Wemm 		p = strchr(buf, '\n');
824c2aa98e2SPeter Wemm 		if (p != NULL)
825c2aa98e2SPeter Wemm 			*p = '\0';
826c2aa98e2SPeter Wemm 		switch (buf[0])
827c2aa98e2SPeter Wemm 		{
828c2aa98e2SPeter Wemm 		  case 'V':		/* version stamp */
829c2aa98e2SPeter Wemm 			ver = atoi(&buf[1]);
830c2aa98e2SPeter Wemm 			if (ver < 0 || ver > 0)
831c2aa98e2SPeter Wemm 				syserr("Unknown host status version %d: %d max",
832c2aa98e2SPeter Wemm 					ver, 0);
833c2aa98e2SPeter Wemm 			break;
834c2aa98e2SPeter Wemm 
835c2aa98e2SPeter Wemm 		  case 'E':		/* UNIX error number */
836c2aa98e2SPeter Wemm 			mci->mci_errno = atoi(&buf[1]);
837c2aa98e2SPeter Wemm 			break;
838c2aa98e2SPeter Wemm 
839c2aa98e2SPeter Wemm 		  case 'H':		/* DNS error number */
840c2aa98e2SPeter Wemm 			mci->mci_herrno = atoi(&buf[1]);
841c2aa98e2SPeter Wemm 			break;
842c2aa98e2SPeter Wemm 
843c2aa98e2SPeter Wemm 		  case 'S':		/* UNIX exit status */
844c2aa98e2SPeter Wemm 			mci->mci_exitstat = atoi(&buf[1]);
845c2aa98e2SPeter Wemm 			break;
846c2aa98e2SPeter Wemm 
847c2aa98e2SPeter Wemm 		  case 'D':		/* DSN status */
848c2aa98e2SPeter Wemm 			mci->mci_status = newstr(&buf[1]);
849c2aa98e2SPeter Wemm 			break;
850c2aa98e2SPeter Wemm 
851c2aa98e2SPeter Wemm 		  case 'R':		/* SMTP status */
852c2aa98e2SPeter Wemm 			mci->mci_rstatus = newstr(&buf[1]);
853c2aa98e2SPeter Wemm 			break;
854c2aa98e2SPeter Wemm 
855c2aa98e2SPeter Wemm 		  case 'U':		/* last usage time */
856c2aa98e2SPeter Wemm 			mci->mci_lastuse = atol(&buf[1]);
857c2aa98e2SPeter Wemm 			break;
858c2aa98e2SPeter Wemm 
859c2aa98e2SPeter Wemm 		  case '.':		/* end of file */
860c2aa98e2SPeter Wemm 			return 0;
861c2aa98e2SPeter Wemm 
862c2aa98e2SPeter Wemm 		  default:
863c2aa98e2SPeter Wemm 			sm_syslog(LOG_CRIT, NOQID,
864c2aa98e2SPeter Wemm 				  "%s: line %d: Unknown host status line \"%s\"",
865c2aa98e2SPeter Wemm 				  FileName == NULL ? mci->mci_host : FileName,
866c2aa98e2SPeter Wemm 				  LineNumber, buf);
867c2aa98e2SPeter Wemm 			LineNumber = saveLineNumber;
868c2aa98e2SPeter Wemm 			return -1;
869c2aa98e2SPeter Wemm 		}
870c2aa98e2SPeter Wemm 	}
871c2aa98e2SPeter Wemm 	LineNumber = saveLineNumber;
872c2aa98e2SPeter Wemm 	if (ver < 0)
873c2aa98e2SPeter Wemm 		return -1;
874c2aa98e2SPeter Wemm 	return 0;
875c2aa98e2SPeter Wemm }
876c2aa98e2SPeter Wemm /*
877c2aa98e2SPeter Wemm **  MCI_STORE_PERSISTENT -- Store persistent MCI information
878c2aa98e2SPeter Wemm **
879c2aa98e2SPeter Wemm **	Store information about host that is kept
880c2aa98e2SPeter Wemm **	in common for all running sendmails.
881c2aa98e2SPeter Wemm **
882c2aa98e2SPeter Wemm **	Parameters:
883c2aa98e2SPeter Wemm **		mci -- the host/connection to store persistent info for.
884c2aa98e2SPeter Wemm **
885c2aa98e2SPeter Wemm **	Returns:
886c2aa98e2SPeter Wemm **		none.
887c2aa98e2SPeter Wemm */
888c2aa98e2SPeter Wemm 
889c2aa98e2SPeter Wemm void
890c2aa98e2SPeter Wemm mci_store_persistent(mci)
891c2aa98e2SPeter Wemm 	MCI *mci;
892c2aa98e2SPeter Wemm {
8933299c2f1SGregory Neil Shapiro 	int save_errno = errno;
894c2aa98e2SPeter Wemm 
895c2aa98e2SPeter Wemm 	if (mci == NULL)
896c2aa98e2SPeter Wemm 	{
897c2aa98e2SPeter Wemm 		if (tTd(56, 1))
8983299c2f1SGregory Neil Shapiro 			dprintf("mci_store_persistent: NULL mci\n");
899c2aa98e2SPeter Wemm 		return;
900c2aa98e2SPeter Wemm 	}
901c2aa98e2SPeter Wemm 
902c2aa98e2SPeter Wemm 	if (HostStatDir == NULL || mci->mci_host == NULL)
903c2aa98e2SPeter Wemm 		return;
904c2aa98e2SPeter Wemm 
905c2aa98e2SPeter Wemm 	if (tTd(56, 1))
9063299c2f1SGregory Neil Shapiro 		dprintf("mci_store_persistent: Storing information for %s\n",
907c2aa98e2SPeter Wemm 			mci->mci_host);
908c2aa98e2SPeter Wemm 
909c2aa98e2SPeter Wemm 	if (mci->mci_statfile == NULL)
910c2aa98e2SPeter Wemm 	{
911c2aa98e2SPeter Wemm 		if (tTd(56, 1))
9123299c2f1SGregory Neil Shapiro 			dprintf("mci_store_persistent: no statfile\n");
913c2aa98e2SPeter Wemm 		return;
914c2aa98e2SPeter Wemm 	}
915c2aa98e2SPeter Wemm 
916c2aa98e2SPeter Wemm 	rewind(mci->mci_statfile);
917c2aa98e2SPeter Wemm #if !NOFTRUNCATE
918c2aa98e2SPeter Wemm 	(void) ftruncate(fileno(mci->mci_statfile), (off_t) 0);
9193299c2f1SGregory Neil Shapiro #endif /* !NOFTRUNCATE */
920c2aa98e2SPeter Wemm 
921c2aa98e2SPeter Wemm 	fprintf(mci->mci_statfile, "V0\n");
922c2aa98e2SPeter Wemm 	fprintf(mci->mci_statfile, "E%d\n", mci->mci_errno);
923c2aa98e2SPeter Wemm 	fprintf(mci->mci_statfile, "H%d\n", mci->mci_herrno);
924c2aa98e2SPeter Wemm 	fprintf(mci->mci_statfile, "S%d\n", mci->mci_exitstat);
925c2aa98e2SPeter Wemm 	if (mci->mci_status != NULL)
926c2aa98e2SPeter Wemm 		fprintf(mci->mci_statfile, "D%.80s\n",
927c2aa98e2SPeter Wemm 			denlstring(mci->mci_status, TRUE, FALSE));
928c2aa98e2SPeter Wemm 	if (mci->mci_rstatus != NULL)
929c2aa98e2SPeter Wemm 		fprintf(mci->mci_statfile, "R%.80s\n",
930c2aa98e2SPeter Wemm 			denlstring(mci->mci_rstatus, TRUE, FALSE));
931c2aa98e2SPeter Wemm 	fprintf(mci->mci_statfile, "U%ld\n", (long)(mci->mci_lastuse));
932c2aa98e2SPeter Wemm 	fprintf(mci->mci_statfile, ".\n");
933c2aa98e2SPeter Wemm 
9343299c2f1SGregory Neil Shapiro 	(void) fflush(mci->mci_statfile);
935c2aa98e2SPeter Wemm 
9363299c2f1SGregory Neil Shapiro 	errno = save_errno;
937c2aa98e2SPeter Wemm 	return;
938c2aa98e2SPeter Wemm }
939c2aa98e2SPeter Wemm /*
940c2aa98e2SPeter Wemm **  MCI_TRAVERSE_PERSISTENT -- walk persistent status tree
941c2aa98e2SPeter Wemm **
942c2aa98e2SPeter Wemm **	Recursively find all the mci host files in `pathname'.  Default to
943c2aa98e2SPeter Wemm **		main host status directory if no path is provided.
944c2aa98e2SPeter Wemm **	Call (*action)(pathname, host) for each file found.
945c2aa98e2SPeter Wemm **
946c2aa98e2SPeter Wemm **	Note: all information is collected in a list before it is processed.
947c2aa98e2SPeter Wemm **	This may not be the best way to do it, but it seems safest, since
948c2aa98e2SPeter Wemm **	the file system would be touched while we are attempting to traverse
949c2aa98e2SPeter Wemm **	the directory tree otherwise (during purges).
950c2aa98e2SPeter Wemm **
951c2aa98e2SPeter Wemm **	Parameters:
952c2aa98e2SPeter Wemm **		action -- function to call on each node.  If returns < 0,
953c2aa98e2SPeter Wemm **			return immediately.
954c2aa98e2SPeter Wemm **		pathname -- root of tree.  If null, use main host status
955c2aa98e2SPeter Wemm **			directory.
956c2aa98e2SPeter Wemm **
957c2aa98e2SPeter Wemm **	Returns:
958c2aa98e2SPeter Wemm **		< 0 -- if any action routine returns a negative value, that
959c2aa98e2SPeter Wemm **			value is returned.
960c2aa98e2SPeter Wemm **		0 -- if we successfully went to completion.
9613299c2f1SGregory Neil Shapiro **		> 0 -- return status from action()
962c2aa98e2SPeter Wemm */
963c2aa98e2SPeter Wemm 
964c2aa98e2SPeter Wemm int
965c2aa98e2SPeter Wemm mci_traverse_persistent(action, pathname)
966c2aa98e2SPeter Wemm 	int (*action)();
967c2aa98e2SPeter Wemm 	char *pathname;
968c2aa98e2SPeter Wemm {
969c2aa98e2SPeter Wemm 	struct stat statbuf;
970c2aa98e2SPeter Wemm 	DIR *d;
971c2aa98e2SPeter Wemm 	int ret;
972c2aa98e2SPeter Wemm 
973c2aa98e2SPeter Wemm 	if (pathname == NULL)
974c2aa98e2SPeter Wemm 		pathname = HostStatDir;
975c2aa98e2SPeter Wemm 	if (pathname == NULL)
976c2aa98e2SPeter Wemm 		return -1;
977c2aa98e2SPeter Wemm 
978c2aa98e2SPeter Wemm 	if (tTd(56, 1))
9793299c2f1SGregory Neil Shapiro 		dprintf("mci_traverse: pathname is %s\n", pathname);
980c2aa98e2SPeter Wemm 
981c2aa98e2SPeter Wemm 	ret = stat(pathname, &statbuf);
982c2aa98e2SPeter Wemm 	if (ret < 0)
983c2aa98e2SPeter Wemm 	{
984c2aa98e2SPeter Wemm 		if (tTd(56, 2))
9853299c2f1SGregory Neil Shapiro 			dprintf("mci_traverse: Failed to stat %s: %s\n",
986c2aa98e2SPeter Wemm 				pathname, errstring(errno));
987c2aa98e2SPeter Wemm 		return ret;
988c2aa98e2SPeter Wemm 	}
989c2aa98e2SPeter Wemm 	if (S_ISDIR(statbuf.st_mode))
990c2aa98e2SPeter Wemm 	{
991c2aa98e2SPeter Wemm 		struct dirent *e;
992c2aa98e2SPeter Wemm 		char *newptr;
993c2aa98e2SPeter Wemm 		char newpath[MAXPATHLEN + 1];
9943299c2f1SGregory Neil Shapiro 		bool leftone, removedone;
995c2aa98e2SPeter Wemm 
996c2aa98e2SPeter Wemm 		if ((d = opendir(pathname)) == NULL)
997c2aa98e2SPeter Wemm 		{
998c2aa98e2SPeter Wemm 			if (tTd(56, 2))
9993299c2f1SGregory Neil Shapiro 				dprintf("mci_traverse: opendir %s: %s\n",
1000c2aa98e2SPeter Wemm 					pathname, errstring(errno));
1001c2aa98e2SPeter Wemm 			return -1;
1002c2aa98e2SPeter Wemm 		}
1003c2aa98e2SPeter Wemm 
1004c2aa98e2SPeter Wemm 		if (strlen(pathname) >= sizeof newpath - MAXNAMLEN - 3)
1005c2aa98e2SPeter Wemm 		{
1006c2aa98e2SPeter Wemm 			if (tTd(56, 2))
10073299c2f1SGregory Neil Shapiro 				dprintf("mci_traverse: path \"%s\" too long",
1008c2aa98e2SPeter Wemm 					pathname);
1009c2aa98e2SPeter Wemm 			return -1;
1010c2aa98e2SPeter Wemm 		}
10113299c2f1SGregory Neil Shapiro 		(void) strlcpy(newpath, pathname, sizeof newpath);
1012c2aa98e2SPeter Wemm 		newptr = newpath + strlen(newpath);
1013c2aa98e2SPeter Wemm 		*newptr++ = '/';
1014c2aa98e2SPeter Wemm 
10153299c2f1SGregory Neil Shapiro 		/*
10163299c2f1SGregory Neil Shapiro 		**  repeat until no file has been removed
10173299c2f1SGregory Neil Shapiro 		**  this may become ugly when several files "expire"
10183299c2f1SGregory Neil Shapiro 		**  during these loops, but it's better than doing
10193299c2f1SGregory Neil Shapiro 		**  a rewinddir() inside the inner loop
10203299c2f1SGregory Neil Shapiro 		*/
10213299c2f1SGregory Neil Shapiro 		do
10223299c2f1SGregory Neil Shapiro 		{
10233299c2f1SGregory Neil Shapiro 			leftone = removedone = FALSE;
1024c2aa98e2SPeter Wemm 			while ((e = readdir(d)) != NULL)
1025c2aa98e2SPeter Wemm 			{
1026c2aa98e2SPeter Wemm 				if (e->d_name[0] == '.')
1027c2aa98e2SPeter Wemm 					continue;
1028c2aa98e2SPeter Wemm 
10293299c2f1SGregory Neil Shapiro 				(void) strlcpy(newptr, e->d_name,
10303299c2f1SGregory Neil Shapiro 					       sizeof newpath -
10313299c2f1SGregory Neil Shapiro 					       (newptr - newpath));
1032c2aa98e2SPeter Wemm 
1033c2aa98e2SPeter Wemm 				ret = mci_traverse_persistent(action, newpath);
1034c2aa98e2SPeter Wemm 				if (ret < 0)
1035c2aa98e2SPeter Wemm 					break;
10363299c2f1SGregory Neil Shapiro 				if (ret == 1)
10373299c2f1SGregory Neil Shapiro 					leftone = TRUE;
10383299c2f1SGregory Neil Shapiro 				if (!removedone && ret == 0 &&
10393299c2f1SGregory Neil Shapiro 				    action == mci_purge_persistent)
10403299c2f1SGregory Neil Shapiro 					removedone = TRUE;
10413299c2f1SGregory Neil Shapiro 			}
10423299c2f1SGregory Neil Shapiro 			if (ret < 0)
10433299c2f1SGregory Neil Shapiro 				break;
1044c2aa98e2SPeter Wemm 			/*
1045c2aa98e2SPeter Wemm 			**  The following appears to be
1046c2aa98e2SPeter Wemm 			**  necessary during purges, since
1047c2aa98e2SPeter Wemm 			**  we modify the directory structure
1048c2aa98e2SPeter Wemm 			*/
10493299c2f1SGregory Neil Shapiro 			if (removedone)
1050c2aa98e2SPeter Wemm 				rewinddir(d);
10513299c2f1SGregory Neil Shapiro 			if (tTd(56, 40))
10523299c2f1SGregory Neil Shapiro 				dprintf("mci_traverse: path %s: ret %d removed %d left %d\n",
10533299c2f1SGregory Neil Shapiro 					pathname, ret, removedone, leftone);
10543299c2f1SGregory Neil Shapiro 		} while (removedone);
1055c2aa98e2SPeter Wemm 
1056c2aa98e2SPeter Wemm 		/* purge (or whatever) the directory proper */
10573299c2f1SGregory Neil Shapiro 		if (!leftone)
10583299c2f1SGregory Neil Shapiro 		{
1059c2aa98e2SPeter Wemm 			*--newptr = '\0';
1060c2aa98e2SPeter Wemm 			ret = (*action)(newpath, NULL);
10613299c2f1SGregory Neil Shapiro 		}
10623299c2f1SGregory Neil Shapiro 		(void) closedir(d);
1063c2aa98e2SPeter Wemm 	}
1064c2aa98e2SPeter Wemm 	else if (S_ISREG(statbuf.st_mode))
1065c2aa98e2SPeter Wemm 	{
1066c2aa98e2SPeter Wemm 		char *end = pathname + strlen(pathname) - 1;
1067c2aa98e2SPeter Wemm 		char *start;
1068c2aa98e2SPeter Wemm 		char *scan;
1069c2aa98e2SPeter Wemm 		char host[MAXHOSTNAMELEN];
1070c2aa98e2SPeter Wemm 		char *hostptr = host;
1071c2aa98e2SPeter Wemm 
1072c2aa98e2SPeter Wemm 		/*
1073c2aa98e2SPeter Wemm 		**  Reconstruct the host name from the path to the
1074c2aa98e2SPeter Wemm 		**  persistent information.
1075c2aa98e2SPeter Wemm 		*/
1076c2aa98e2SPeter Wemm 
1077c2aa98e2SPeter Wemm 		do
1078c2aa98e2SPeter Wemm 		{
1079c2aa98e2SPeter Wemm 			if (hostptr != host)
1080c2aa98e2SPeter Wemm 				*(hostptr++) = '.';
1081c2aa98e2SPeter Wemm 			start = end;
1082c2aa98e2SPeter Wemm 			while (*(start - 1) != '/')
1083c2aa98e2SPeter Wemm 				start--;
1084c2aa98e2SPeter Wemm 
1085c2aa98e2SPeter Wemm 			if (*end == '.')
1086c2aa98e2SPeter Wemm 				end--;
1087c2aa98e2SPeter Wemm 
1088c2aa98e2SPeter Wemm 			for (scan = start; scan <= end; scan++)
1089c2aa98e2SPeter Wemm 				*(hostptr++) = *scan;
1090c2aa98e2SPeter Wemm 
1091c2aa98e2SPeter Wemm 			end = start - 2;
1092c2aa98e2SPeter Wemm 		} while (*end == '.');
1093c2aa98e2SPeter Wemm 
1094c2aa98e2SPeter Wemm 		*hostptr = '\0';
1095c2aa98e2SPeter Wemm 
1096c2aa98e2SPeter Wemm 		/*
1097c2aa98e2SPeter Wemm 		**  Do something with the file containing the persistent
1098c2aa98e2SPeter Wemm 		**  information.
1099c2aa98e2SPeter Wemm 		*/
1100c2aa98e2SPeter Wemm 		ret = (*action)(pathname, host);
1101c2aa98e2SPeter Wemm 	}
1102c2aa98e2SPeter Wemm 
1103c2aa98e2SPeter Wemm 	return ret;
1104c2aa98e2SPeter Wemm }
1105c2aa98e2SPeter Wemm /*
11063299c2f1SGregory Neil Shapiro **  MCI_PRINT_PERSISTENT -- print persistent info
1107c2aa98e2SPeter Wemm **
1108c2aa98e2SPeter Wemm **	Dump the persistent information in the file 'pathname'
1109c2aa98e2SPeter Wemm **
1110c2aa98e2SPeter Wemm **	Parameters:
1111c2aa98e2SPeter Wemm **		pathname -- the pathname to the status file.
1112c2aa98e2SPeter Wemm **		hostname -- the corresponding host name.
1113c2aa98e2SPeter Wemm **
1114c2aa98e2SPeter Wemm **	Returns:
1115c2aa98e2SPeter Wemm **		0
1116c2aa98e2SPeter Wemm */
1117c2aa98e2SPeter Wemm 
1118c2aa98e2SPeter Wemm int
1119c2aa98e2SPeter Wemm mci_print_persistent(pathname, hostname)
1120c2aa98e2SPeter Wemm 	char *pathname;
1121c2aa98e2SPeter Wemm 	char *hostname;
1122c2aa98e2SPeter Wemm {
1123c2aa98e2SPeter Wemm 	static int initflag = FALSE;
1124c2aa98e2SPeter Wemm 	FILE *fp;
1125c2aa98e2SPeter Wemm 	int width = Verbose ? 78 : 25;
1126c2aa98e2SPeter Wemm 	bool locked;
1127c2aa98e2SPeter Wemm 	MCI mcib;
1128c2aa98e2SPeter Wemm 
1129c2aa98e2SPeter Wemm 	/* skip directories */
1130c2aa98e2SPeter Wemm 	if (hostname == NULL)
1131c2aa98e2SPeter Wemm 		return 0;
1132c2aa98e2SPeter Wemm 
1133c2aa98e2SPeter Wemm 	if (!initflag)
1134c2aa98e2SPeter Wemm 	{
1135c2aa98e2SPeter Wemm 		initflag = TRUE;
1136c2aa98e2SPeter Wemm 		printf(" -------------- Hostname --------------- How long ago ---------Results---------\n");
1137c2aa98e2SPeter Wemm 	}
1138c2aa98e2SPeter Wemm 
1139c2aa98e2SPeter Wemm 	fp = safefopen(pathname, O_RDWR, FileMode,
1140c2aa98e2SPeter Wemm 		       SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH);
1141c2aa98e2SPeter Wemm 
1142c2aa98e2SPeter Wemm 	if (fp == NULL)
1143c2aa98e2SPeter Wemm 	{
1144c2aa98e2SPeter Wemm 		if (tTd(56, 1))
11453299c2f1SGregory Neil Shapiro 			dprintf("mci_print_persistent: cannot open %s: %s\n",
1146c2aa98e2SPeter Wemm 				pathname, errstring(errno));
1147c2aa98e2SPeter Wemm 		return 0;
1148c2aa98e2SPeter Wemm 	}
1149c2aa98e2SPeter Wemm 
1150c2aa98e2SPeter Wemm 	FileName = pathname;
11513299c2f1SGregory Neil Shapiro 	memset(&mcib, '\0', sizeof mcib);
1152c2aa98e2SPeter Wemm 	if (mci_read_persistent(fp, &mcib) < 0)
1153c2aa98e2SPeter Wemm 	{
1154c2aa98e2SPeter Wemm 		syserr("%s: could not read status file", pathname);
11553299c2f1SGregory Neil Shapiro 		(void) fclose(fp);
1156c2aa98e2SPeter Wemm 		FileName = NULL;
1157c2aa98e2SPeter Wemm 		return 0;
1158c2aa98e2SPeter Wemm 	}
1159c2aa98e2SPeter Wemm 
1160c2aa98e2SPeter Wemm 	locked = !lockfile(fileno(fp), pathname, "", LOCK_EX|LOCK_NB);
11613299c2f1SGregory Neil Shapiro 	(void) fclose(fp);
1162c2aa98e2SPeter Wemm 	FileName = NULL;
1163c2aa98e2SPeter Wemm 
1164c2aa98e2SPeter Wemm 	printf("%c%-39s %12s ",
1165c2aa98e2SPeter Wemm 		locked ? '*' : ' ', hostname,
1166c2aa98e2SPeter Wemm 		pintvl(curtime() - mcib.mci_lastuse, TRUE));
1167c2aa98e2SPeter Wemm 	if (mcib.mci_rstatus != NULL)
1168c2aa98e2SPeter Wemm 		printf("%.*s\n", width, mcib.mci_rstatus);
1169c2aa98e2SPeter Wemm 	else if (mcib.mci_exitstat == EX_TEMPFAIL && mcib.mci_errno != 0)
1170c2aa98e2SPeter Wemm 		printf("Deferred: %.*s\n", width - 10, errstring(mcib.mci_errno));
1171c2aa98e2SPeter Wemm 	else if (mcib.mci_exitstat != 0)
1172c2aa98e2SPeter Wemm 	{
1173c2aa98e2SPeter Wemm 		int i = mcib.mci_exitstat - EX__BASE;
1174c2aa98e2SPeter Wemm 		extern int N_SysEx;
1175c2aa98e2SPeter Wemm 		extern char *SysExMsg[];
1176c2aa98e2SPeter Wemm 
1177c2aa98e2SPeter Wemm 		if (i < 0 || i >= N_SysEx)
1178c2aa98e2SPeter Wemm 		{
1179c2aa98e2SPeter Wemm 			char buf[80];
1180c2aa98e2SPeter Wemm 
1181c2aa98e2SPeter Wemm 			snprintf(buf, sizeof buf, "Unknown mailer error %d",
1182c2aa98e2SPeter Wemm 				mcib.mci_exitstat);
1183c2aa98e2SPeter Wemm 			printf("%.*s\n", width, buf);
1184c2aa98e2SPeter Wemm 		}
1185c2aa98e2SPeter Wemm 		else
1186c2aa98e2SPeter Wemm 			printf("%.*s\n", width, &(SysExMsg[i])[5]);
1187c2aa98e2SPeter Wemm 	}
1188c2aa98e2SPeter Wemm 	else if (mcib.mci_errno == 0)
1189c2aa98e2SPeter Wemm 		printf("OK\n");
1190c2aa98e2SPeter Wemm 	else
1191c2aa98e2SPeter Wemm 		printf("OK: %.*s\n", width - 4, errstring(mcib.mci_errno));
1192c2aa98e2SPeter Wemm 
1193c2aa98e2SPeter Wemm 	return 0;
1194c2aa98e2SPeter Wemm }
1195c2aa98e2SPeter Wemm /*
1196c2aa98e2SPeter Wemm **  MCI_PURGE_PERSISTENT -- Remove a persistence status file.
1197c2aa98e2SPeter Wemm **
1198c2aa98e2SPeter Wemm **	Parameters:
1199c2aa98e2SPeter Wemm **		pathname -- path to the status file.
1200c2aa98e2SPeter Wemm **		hostname -- name of host corresponding to that file.
1201c2aa98e2SPeter Wemm **			NULL if this is a directory (domain).
1202c2aa98e2SPeter Wemm **
1203c2aa98e2SPeter Wemm **	Returns:
12043299c2f1SGregory Neil Shapiro **		0 -- ok
12053299c2f1SGregory Neil Shapiro **		1 -- file not deleted (too young, incorrect format)
12063299c2f1SGregory Neil Shapiro **		< 0 -- some error occurred
1207c2aa98e2SPeter Wemm */
1208c2aa98e2SPeter Wemm 
1209c2aa98e2SPeter Wemm int
1210c2aa98e2SPeter Wemm mci_purge_persistent(pathname, hostname)
1211c2aa98e2SPeter Wemm 	char *pathname;
1212c2aa98e2SPeter Wemm 	char *hostname;
1213c2aa98e2SPeter Wemm {
12143299c2f1SGregory Neil Shapiro 	struct stat statbuf;
1215c2aa98e2SPeter Wemm 	char *end = pathname + strlen(pathname) - 1;
12163299c2f1SGregory Neil Shapiro 	int ret;
1217c2aa98e2SPeter Wemm 
1218c2aa98e2SPeter Wemm 	if (tTd(56, 1))
12193299c2f1SGregory Neil Shapiro 		dprintf("mci_purge_persistent: purging %s\n", pathname);
1220c2aa98e2SPeter Wemm 
12213299c2f1SGregory Neil Shapiro 	ret = stat(pathname, &statbuf);
12223299c2f1SGregory Neil Shapiro 	if (ret < 0)
12233299c2f1SGregory Neil Shapiro 	{
12243299c2f1SGregory Neil Shapiro 		if (tTd(56, 2))
12253299c2f1SGregory Neil Shapiro 			dprintf("mci_purge_persistent: Failed to stat %s: %s\n",
12263299c2f1SGregory Neil Shapiro 				pathname, errstring(errno));
12273299c2f1SGregory Neil Shapiro 		return ret;
12283299c2f1SGregory Neil Shapiro 	}
12293299c2f1SGregory Neil Shapiro 	if (curtime() - statbuf.st_mtime < MciInfoTimeout)
12303299c2f1SGregory Neil Shapiro 		return 1;
1231c2aa98e2SPeter Wemm 	if (hostname != NULL)
1232c2aa98e2SPeter Wemm 	{
1233c2aa98e2SPeter Wemm 		/* remove the file */
1234c2aa98e2SPeter Wemm 		if (unlink(pathname) < 0)
1235c2aa98e2SPeter Wemm 		{
1236c2aa98e2SPeter Wemm 			if (tTd(56, 2))
12373299c2f1SGregory Neil Shapiro 				dprintf("mci_purge_persistent: failed to unlink %s: %s\n",
1238c2aa98e2SPeter Wemm 					pathname, errstring(errno));
1239c2aa98e2SPeter Wemm 		}
1240c2aa98e2SPeter Wemm 	}
1241c2aa98e2SPeter Wemm 	else
1242c2aa98e2SPeter Wemm 	{
1243c2aa98e2SPeter Wemm 		/* remove the directory */
1244c2aa98e2SPeter Wemm 		if (*end != '.')
12453299c2f1SGregory Neil Shapiro 			return 1;
1246c2aa98e2SPeter Wemm 
1247c2aa98e2SPeter Wemm 		if (tTd(56, 1))
12483299c2f1SGregory Neil Shapiro 			dprintf("mci_purge_persistent: dpurge %s\n", pathname);
1249c2aa98e2SPeter Wemm 
1250c2aa98e2SPeter Wemm 		if (rmdir(pathname) < 0)
1251c2aa98e2SPeter Wemm 		{
1252c2aa98e2SPeter Wemm 			if (tTd(56, 2))
12533299c2f1SGregory Neil Shapiro 				dprintf("mci_purge_persistent: rmdir %s: %s\n",
1254c2aa98e2SPeter Wemm 					pathname, errstring(errno));
1255c2aa98e2SPeter Wemm 		}
1256c2aa98e2SPeter Wemm 
1257c2aa98e2SPeter Wemm 	}
1258c2aa98e2SPeter Wemm 
1259c2aa98e2SPeter Wemm 	return 0;
1260c2aa98e2SPeter Wemm }
1261c2aa98e2SPeter Wemm /*
1262c2aa98e2SPeter Wemm **  MCI_GENERATE_PERSISTENT_PATH -- generate path from hostname
1263c2aa98e2SPeter Wemm **
1264c2aa98e2SPeter Wemm **	Given `host', convert from a.b.c to $QueueDir/.hoststat/c./b./a,
1265c2aa98e2SPeter Wemm **	putting the result into `path'.  if `createflag' is set, intervening
1266c2aa98e2SPeter Wemm **	directories will be created as needed.
1267c2aa98e2SPeter Wemm **
1268c2aa98e2SPeter Wemm **	Parameters:
1269c2aa98e2SPeter Wemm **		host -- host name to convert from.
1270c2aa98e2SPeter Wemm **		path -- place to store result.
1271c2aa98e2SPeter Wemm **		pathlen -- length of path buffer.
1272c2aa98e2SPeter Wemm **		createflag -- if set, create intervening directories as
1273c2aa98e2SPeter Wemm **			needed.
1274c2aa98e2SPeter Wemm **
1275c2aa98e2SPeter Wemm **	Returns:
1276c2aa98e2SPeter Wemm **		0 -- success
1277c2aa98e2SPeter Wemm **		-1 -- failure
1278c2aa98e2SPeter Wemm */
1279c2aa98e2SPeter Wemm 
12803299c2f1SGregory Neil Shapiro static int
1281c2aa98e2SPeter Wemm mci_generate_persistent_path(host, path, pathlen, createflag)
1282c2aa98e2SPeter Wemm 	const char *host;
1283c2aa98e2SPeter Wemm 	char *path;
1284c2aa98e2SPeter Wemm 	int pathlen;
1285c2aa98e2SPeter Wemm 	bool createflag;
1286c2aa98e2SPeter Wemm {
1287c2aa98e2SPeter Wemm 	char *elem, *p, *x, ch;
1288c2aa98e2SPeter Wemm 	int ret = 0;
1289c2aa98e2SPeter Wemm 	int len;
1290c2aa98e2SPeter Wemm 	char t_host[MAXHOSTNAMELEN];
12913299c2f1SGregory Neil Shapiro #if NETINET6
12923299c2f1SGregory Neil Shapiro 	struct in6_addr in6_addr;
12933299c2f1SGregory Neil Shapiro #endif /* NETINET6 */
1294c2aa98e2SPeter Wemm 
1295c2aa98e2SPeter Wemm 	/*
1296c2aa98e2SPeter Wemm 	**  Rationality check the arguments.
1297c2aa98e2SPeter Wemm 	*/
1298c2aa98e2SPeter Wemm 
1299c2aa98e2SPeter Wemm 	if (host == NULL)
1300c2aa98e2SPeter Wemm 	{
1301c2aa98e2SPeter Wemm 		syserr("mci_generate_persistent_path: null host");
1302c2aa98e2SPeter Wemm 		return -1;
1303c2aa98e2SPeter Wemm 	}
1304c2aa98e2SPeter Wemm 	if (path == NULL)
1305c2aa98e2SPeter Wemm 	{
1306c2aa98e2SPeter Wemm 		syserr("mci_generate_persistent_path: null path");
1307c2aa98e2SPeter Wemm 		return -1;
1308c2aa98e2SPeter Wemm 	}
1309c2aa98e2SPeter Wemm 
1310c2aa98e2SPeter Wemm 	if (tTd(56, 80))
13113299c2f1SGregory Neil Shapiro 		dprintf("mci_generate_persistent_path(%s): ", host);
1312c2aa98e2SPeter Wemm 
1313c2aa98e2SPeter Wemm 	if (*host == '\0' || *host == '.')
1314c2aa98e2SPeter Wemm 		return -1;
1315c2aa98e2SPeter Wemm 
1316c2aa98e2SPeter Wemm 	/* make certain this is not a bracketed host number */
1317c2aa98e2SPeter Wemm 	if (strlen(host) > sizeof t_host - 1)
1318c2aa98e2SPeter Wemm 		return -1;
1319c2aa98e2SPeter Wemm 	if (host[0] == '[')
13203299c2f1SGregory Neil Shapiro 		(void) strlcpy(t_host, host + 1, sizeof t_host);
1321c2aa98e2SPeter Wemm 	else
13223299c2f1SGregory Neil Shapiro 		(void) strlcpy(t_host, host, sizeof t_host);
1323c2aa98e2SPeter Wemm 
1324c2aa98e2SPeter Wemm 	/*
1325c2aa98e2SPeter Wemm 	**  Delete any trailing dots from the hostname.
1326c2aa98e2SPeter Wemm 	**  Leave 'elem' pointing at the \0.
1327c2aa98e2SPeter Wemm 	*/
1328c2aa98e2SPeter Wemm 
1329c2aa98e2SPeter Wemm 	elem = t_host + strlen(t_host);
1330c2aa98e2SPeter Wemm 	while (elem > t_host &&
1331c2aa98e2SPeter Wemm 	       (elem[-1] == '.' || (host[0] == '[' && elem[-1] == ']')))
1332c2aa98e2SPeter Wemm 		*--elem = '\0';
1333c2aa98e2SPeter Wemm 
13343299c2f1SGregory Neil Shapiro #if NETINET || NETINET6
1335c2aa98e2SPeter Wemm 	/* check for bogus bracketed address */
1336c46d91b7SGregory Neil Shapiro 	if (host[0] == '['
13373299c2f1SGregory Neil Shapiro # if NETINET6
1338c46d91b7SGregory Neil Shapiro 	    && inet_pton(AF_INET6, t_host, &in6_addr) != 1
13393299c2f1SGregory Neil Shapiro # endif /* NETINET6 */
13403299c2f1SGregory Neil Shapiro # if NETINET
1341c46d91b7SGregory Neil Shapiro 	    && inet_addr(t_host) == INADDR_NONE
13423299c2f1SGregory Neil Shapiro # endif /* NETINET */
13433299c2f1SGregory Neil Shapiro 	    )
1344c2aa98e2SPeter Wemm 		return -1;
13453299c2f1SGregory Neil Shapiro #endif /* NETINET || NETINET6 */
1346c2aa98e2SPeter Wemm 
1347c2aa98e2SPeter Wemm 	/* check for what will be the final length of the path */
1348c2aa98e2SPeter Wemm 	len = strlen(HostStatDir) + 2;
1349c2aa98e2SPeter Wemm 	for (p = (char *) t_host; *p != '\0'; p++)
1350c2aa98e2SPeter Wemm 	{
1351c2aa98e2SPeter Wemm 		if (*p == '.')
1352c2aa98e2SPeter Wemm 			len++;
1353c2aa98e2SPeter Wemm 		len++;
1354c2aa98e2SPeter Wemm 		if (p[0] == '.' && p[1] == '.')
1355c2aa98e2SPeter Wemm 			return -1;
1356c2aa98e2SPeter Wemm 	}
1357c2aa98e2SPeter Wemm 	if (len > pathlen || len < 1)
1358c2aa98e2SPeter Wemm 		return -1;
1359c2aa98e2SPeter Wemm 
13603299c2f1SGregory Neil Shapiro 	(void) strlcpy(path, HostStatDir, pathlen);
1361c2aa98e2SPeter Wemm 	p = path + strlen(path);
1362c2aa98e2SPeter Wemm 
1363c2aa98e2SPeter Wemm 	while (elem > t_host)
1364c2aa98e2SPeter Wemm 	{
1365c2aa98e2SPeter Wemm 		if (!path_is_dir(path, createflag))
1366c2aa98e2SPeter Wemm 		{
1367c2aa98e2SPeter Wemm 			ret = -1;
1368c2aa98e2SPeter Wemm 			break;
1369c2aa98e2SPeter Wemm 		}
1370c2aa98e2SPeter Wemm 		elem--;
1371c2aa98e2SPeter Wemm 		while (elem >= t_host && *elem != '.')
1372c2aa98e2SPeter Wemm 			elem--;
1373c2aa98e2SPeter Wemm 		*p++ = '/';
1374c2aa98e2SPeter Wemm 		x = elem + 1;
1375c2aa98e2SPeter Wemm 		while ((ch = *x++) != '\0' && ch != '.')
1376c2aa98e2SPeter Wemm 		{
1377c2aa98e2SPeter Wemm 			if (isascii(ch) && isupper(ch))
1378c2aa98e2SPeter Wemm 				ch = tolower(ch);
1379c2aa98e2SPeter Wemm 			if (ch == '/')
1380c2aa98e2SPeter Wemm 				ch = ':';	/* / -> : */
1381c2aa98e2SPeter Wemm 			*p++ = ch;
1382c2aa98e2SPeter Wemm 		}
1383c2aa98e2SPeter Wemm 		if (elem >= t_host)
1384c2aa98e2SPeter Wemm 			*p++ = '.';
1385c2aa98e2SPeter Wemm 		*p = '\0';
1386c2aa98e2SPeter Wemm 	}
1387c2aa98e2SPeter Wemm 
1388c2aa98e2SPeter Wemm 	if (tTd(56, 80))
1389c2aa98e2SPeter Wemm 	{
1390c2aa98e2SPeter Wemm 		if (ret < 0)
13913299c2f1SGregory Neil Shapiro 			dprintf("FAILURE %d\n", ret);
1392c2aa98e2SPeter Wemm 		else
13933299c2f1SGregory Neil Shapiro 			dprintf("SUCCESS %s\n", path);
1394c2aa98e2SPeter Wemm 	}
1395c2aa98e2SPeter Wemm 
13963299c2f1SGregory Neil Shapiro 	return ret;
1397c2aa98e2SPeter Wemm }
1398