mci.c revision 98125
138032Speter/* 294337Sgshapiro * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers. 364565Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1995-1997 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1988, 1993 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 1238032Speter */ 1338032Speter 1464565Sgshapiro/* $FreeBSD: head/contrib/sendmail/src/mci.c 98125 2002-06-11 21:16:51Z gshapiro $ */ 1564565Sgshapiro 1664565Sgshapiro#include <sendmail.h> 1764565Sgshapiro 1898125SgshapiroSM_RCSID("@(#)$Id: mci.c,v 8.205 2002/05/24 18:53:48 gshapiro Exp $") 1964565Sgshapiro 2064565Sgshapiro#if NETINET || NETINET6 2164565Sgshapiro# include <arpa/inet.h> 2264565Sgshapiro#endif /* NETINET || NETINET6 */ 2364565Sgshapiro 2438032Speter#include <dirent.h> 2538032Speter 2664565Sgshapirostatic int mci_generate_persistent_path __P((const char *, char *, 2764565Sgshapiro int, bool)); 2864565Sgshapirostatic bool mci_load_persistent __P((MCI *)); 2964565Sgshapirostatic void mci_uncache __P((MCI **, bool)); 3064565Sgshapirostatic int mci_lock_host_statfile __P((MCI *)); 3190795Sgshapirostatic int mci_read_persistent __P((SM_FILE_T *, MCI *)); 3264565Sgshapiro 3338032Speter/* 3438032Speter** Mail Connection Information (MCI) Caching Module. 3538032Speter** 3638032Speter** There are actually two separate things cached. The first is 3738032Speter** the set of all open connections -- these are stored in a 3838032Speter** (small) list. The second is stored in the symbol table; it 3938032Speter** has the overall status for all hosts, whether or not there 4038032Speter** is a connection open currently. 4138032Speter** 4238032Speter** There should never be too many connections open (since this 4338032Speter** could flood the socket table), nor should a connection be 4438032Speter** allowed to sit idly for too long. 4538032Speter** 4638032Speter** MaxMciCache is the maximum number of open connections that 4738032Speter** will be supported. 4838032Speter** 4938032Speter** MciCacheTimeout is the time (in seconds) that a connection 5038032Speter** is permitted to survive without activity. 5138032Speter** 5238032Speter** We actually try any cached connections by sending a NOOP 5338032Speter** before we use them; if the NOOP fails we close down the 5438032Speter** connection and reopen it. Note that this means that a 5538032Speter** server SMTP that doesn't support NOOP will hose the 5638032Speter** algorithm -- but that doesn't seem too likely. 5738032Speter** 5838032Speter** The persistent MCI code is donated by Mark Lovell and Paul 5938032Speter** Vixie. It is based on the long term host status code in KJS 6038032Speter** written by Paul but has been adapted by Mark to fit into the 6138032Speter** MCI structure. 6238032Speter*/ 6338032Speter 6464565Sgshapirostatic MCI **MciCache; /* the open connection cache */ 6538032Speter 6690795Sgshapiro/* 6738032Speter** MCI_CACHE -- enter a connection structure into the open connection cache 6838032Speter** 6938032Speter** This may cause something else to be flushed. 7038032Speter** 7138032Speter** Parameters: 7238032Speter** mci -- the connection to cache. 7338032Speter** 7438032Speter** Returns: 7538032Speter** none. 7638032Speter*/ 7738032Speter 7838032Spetervoid 7938032Spetermci_cache(mci) 8038032Speter register MCI *mci; 8138032Speter{ 8238032Speter register MCI **mcislot; 8338032Speter 8438032Speter /* 8538032Speter ** Find the best slot. This may cause expired connections 8638032Speter ** to be closed. 8738032Speter */ 8838032Speter 8938032Speter mcislot = mci_scan(mci); 9038032Speter if (mcislot == NULL) 9138032Speter { 9238032Speter /* we don't support caching */ 9338032Speter return; 9438032Speter } 9538032Speter 9638032Speter if (mci->mci_host == NULL) 9738032Speter return; 9838032Speter 9938032Speter /* if this is already cached, we are done */ 10038032Speter if (bitset(MCIF_CACHED, mci->mci_flags)) 10138032Speter return; 10238032Speter 10338032Speter /* otherwise we may have to clear the slot */ 10438032Speter if (*mcislot != NULL) 10590795Sgshapiro mci_uncache(mcislot, true); 10638032Speter 10738032Speter if (tTd(42, 5)) 10890795Sgshapiro sm_dprintf("mci_cache: caching %p (%s) in slot %d\n", 10990795Sgshapiro mci, mci->mci_host, (int) (mcislot - MciCache)); 11038032Speter if (tTd(91, 100)) 11138032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 11264565Sgshapiro "mci_cache: caching %lx (%.100s) in slot %d", 11390795Sgshapiro (unsigned long) mci, mci->mci_host, 11490795Sgshapiro (int) (mcislot - MciCache)); 11538032Speter 11638032Speter *mcislot = mci; 11738032Speter mci->mci_flags |= MCIF_CACHED; 11838032Speter} 11990795Sgshapiro/* 12038032Speter** MCI_SCAN -- scan the cache, flush junk, and return best slot 12138032Speter** 12238032Speter** Parameters: 12338032Speter** savemci -- never flush this one. Can be null. 12438032Speter** 12538032Speter** Returns: 12638032Speter** The LRU (or empty) slot. 12738032Speter*/ 12838032Speter 12938032SpeterMCI ** 13038032Spetermci_scan(savemci) 13138032Speter MCI *savemci; 13238032Speter{ 13338032Speter time_t now; 13438032Speter register MCI **bestmci; 13538032Speter register MCI *mci; 13638032Speter register int i; 13738032Speter 13838032Speter if (MaxMciCache <= 0) 13938032Speter { 14038032Speter /* we don't support caching */ 14138032Speter return NULL; 14238032Speter } 14338032Speter 14438032Speter if (MciCache == NULL) 14538032Speter { 14638032Speter /* first call */ 14790795Sgshapiro MciCache = (MCI **) sm_pmalloc_x(MaxMciCache * sizeof *MciCache); 14864565Sgshapiro memset((char *) MciCache, '\0', MaxMciCache * sizeof *MciCache); 14964565Sgshapiro return &MciCache[0]; 15038032Speter } 15138032Speter 15238032Speter now = curtime(); 15338032Speter bestmci = &MciCache[0]; 15438032Speter for (i = 0; i < MaxMciCache; i++) 15538032Speter { 15638032Speter mci = MciCache[i]; 15738032Speter if (mci == NULL || mci->mci_state == MCIS_CLOSED) 15838032Speter { 15938032Speter bestmci = &MciCache[i]; 16038032Speter continue; 16138032Speter } 16294337Sgshapiro if ((mci->mci_lastuse + MciCacheTimeout <= now || 16364565Sgshapiro (mci->mci_mailer != NULL && 16464565Sgshapiro mci->mci_mailer->m_maxdeliveries > 0 && 16564565Sgshapiro mci->mci_deliveries + 1 >= mci->mci_mailer->m_maxdeliveries))&& 16664565Sgshapiro mci != savemci) 16738032Speter { 16864565Sgshapiro /* connection idle too long or too many deliveries */ 16938032Speter bestmci = &MciCache[i]; 17064565Sgshapiro 17164565Sgshapiro /* close it */ 17290795Sgshapiro mci_uncache(bestmci, true); 17338032Speter continue; 17438032Speter } 17538032Speter if (*bestmci == NULL) 17638032Speter continue; 17738032Speter if (mci->mci_lastuse < (*bestmci)->mci_lastuse) 17838032Speter bestmci = &MciCache[i]; 17938032Speter } 18038032Speter return bestmci; 18138032Speter} 18290795Sgshapiro/* 18338032Speter** MCI_UNCACHE -- remove a connection from a slot. 18438032Speter** 18538032Speter** May close a connection. 18638032Speter** 18738032Speter** Parameters: 18838032Speter** mcislot -- the slot to empty. 18990795Sgshapiro** doquit -- if true, send QUIT protocol on this connection. 19090795Sgshapiro** if false, we are assumed to be in a forked child; 19138032Speter** all we want to do is close the file(s). 19238032Speter** 19338032Speter** Returns: 19438032Speter** none. 19538032Speter*/ 19638032Speter 19764565Sgshapirostatic void 19838032Spetermci_uncache(mcislot, doquit) 19938032Speter register MCI **mcislot; 20038032Speter bool doquit; 20138032Speter{ 20238032Speter register MCI *mci; 20338032Speter extern ENVELOPE BlankEnvelope; 20438032Speter 20538032Speter mci = *mcislot; 20638032Speter if (mci == NULL) 20738032Speter return; 20838032Speter *mcislot = NULL; 20938032Speter if (mci->mci_host == NULL) 21038032Speter return; 21138032Speter 21238032Speter mci_unlock_host(mci); 21338032Speter 21438032Speter if (tTd(42, 5)) 21590795Sgshapiro sm_dprintf("mci_uncache: uncaching %p (%s) from slot %d (%d)\n", 21690795Sgshapiro mci, mci->mci_host, (int) (mcislot - MciCache), 21790795Sgshapiro doquit); 21838032Speter if (tTd(91, 100)) 21938032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 22064565Sgshapiro "mci_uncache: uncaching %lx (%.100s) from slot %d (%d)", 22190795Sgshapiro (unsigned long) mci, mci->mci_host, 22290795Sgshapiro (int) (mcislot - MciCache), doquit); 22338032Speter 22464565Sgshapiro mci->mci_deliveries = 0; 22538032Speter if (doquit) 22638032Speter { 22738032Speter message("Closing connection to %s", mci->mci_host); 22838032Speter 22938032Speter mci->mci_flags &= ~MCIF_CACHED; 23038032Speter 23138032Speter /* only uses the envelope to flush the transcript file */ 23238032Speter if (mci->mci_state != MCIS_CLOSED) 23338032Speter smtpquit(mci->mci_mailer, mci, &BlankEnvelope); 23490795Sgshapiro#if XLA 23538032Speter xla_host_end(mci->mci_host); 23690795Sgshapiro#endif /* XLA */ 23738032Speter } 23838032Speter else 23938032Speter { 24038032Speter if (mci->mci_in != NULL) 24190795Sgshapiro (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 24238032Speter if (mci->mci_out != NULL) 24390795Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 24438032Speter mci->mci_in = mci->mci_out = NULL; 24538032Speter mci->mci_state = MCIS_CLOSED; 24638032Speter mci->mci_exitstat = EX_OK; 24738032Speter mci->mci_errno = 0; 24838032Speter mci->mci_flags = 0; 24990795Sgshapiro 25090795Sgshapiro mci->mci_retryrcpt = false; 25190795Sgshapiro mci->mci_tolist = NULL; 25290795Sgshapiro#if PIPELINING 25390795Sgshapiro mci->mci_okrcpts = 0; 25490795Sgshapiro#endif /* PIPELINING */ 25538032Speter } 25690795Sgshapiro 25790795Sgshapiro SM_FREE_CLR(mci->mci_status); 25890795Sgshapiro SM_FREE_CLR(mci->mci_rstatus); 25990795Sgshapiro SM_FREE_CLR(mci->mci_heloname); 26090795Sgshapiro if (mci->mci_rpool != NULL) 26190795Sgshapiro { 26290795Sgshapiro sm_rpool_free(mci->mci_rpool); 26390795Sgshapiro mci->mci_macro.mac_rpool = NULL; 26490795Sgshapiro mci->mci_rpool = NULL; 26590795Sgshapiro } 26638032Speter} 26790795Sgshapiro/* 26838032Speter** MCI_FLUSH -- flush the entire cache 26938032Speter** 27038032Speter** Parameters: 27190795Sgshapiro** doquit -- if true, send QUIT protocol. 27290795Sgshapiro** if false, just close the connection. 27338032Speter** allbut -- but leave this one open. 27438032Speter** 27538032Speter** Returns: 27638032Speter** none. 27738032Speter*/ 27838032Speter 27938032Spetervoid 28038032Spetermci_flush(doquit, allbut) 28138032Speter bool doquit; 28238032Speter MCI *allbut; 28338032Speter{ 28438032Speter register int i; 28538032Speter 28638032Speter if (MciCache == NULL) 28738032Speter return; 28838032Speter 28938032Speter for (i = 0; i < MaxMciCache; i++) 29071348Sgshapiro { 29138032Speter if (allbut != MciCache[i]) 29238032Speter mci_uncache(&MciCache[i], doquit); 29371348Sgshapiro } 29438032Speter} 29590795Sgshapiro/* 29638032Speter** MCI_GET -- get information about a particular host 29790795Sgshapiro** 29890795Sgshapiro** Parameters: 29990795Sgshapiro** host -- host to look for. 30090795Sgshapiro** m -- mailer. 30190795Sgshapiro** 30290795Sgshapiro** Returns: 30390795Sgshapiro** mci for this host (might be new). 30438032Speter*/ 30538032Speter 30638032SpeterMCI * 30738032Spetermci_get(host, m) 30838032Speter char *host; 30938032Speter MAILER *m; 31038032Speter{ 31138032Speter register MCI *mci; 31238032Speter register STAB *s; 31338032Speter extern SOCKADDR CurHostAddr; 31438032Speter 31538032Speter /* clear CurHostAddr so we don't get a bogus address with this name */ 31664565Sgshapiro memset(&CurHostAddr, '\0', sizeof CurHostAddr); 31738032Speter 31838032Speter /* clear out any expired connections */ 31938032Speter (void) mci_scan(NULL); 32038032Speter 32138032Speter if (m->m_mno < 0) 32271348Sgshapiro syserr("!negative mno %d (%s)", m->m_mno, m->m_name); 32364565Sgshapiro 32438032Speter s = stab(host, ST_MCI + m->m_mno, ST_ENTER); 32538032Speter mci = &s->s_mci; 32638032Speter 32790795Sgshapiro /* initialize per-message data */ 32890795Sgshapiro mci->mci_retryrcpt = false; 32990795Sgshapiro mci->mci_tolist = NULL; 33090795Sgshapiro#if PIPELINING 33190795Sgshapiro mci->mci_okrcpts = 0; 33290795Sgshapiro#endif /* PIPELINING */ 33390795Sgshapiro 33490795Sgshapiro if (mci->mci_rpool == NULL) 33590795Sgshapiro mci->mci_rpool = sm_rpool_new_x(NULL); 33690795Sgshapiro 33790795Sgshapiro if (mci->mci_macro.mac_rpool == NULL) 33890795Sgshapiro mci->mci_macro.mac_rpool = mci->mci_rpool; 33990795Sgshapiro 34064565Sgshapiro /* 34190795Sgshapiro ** We don't need to load the persistent data if we have data 34264565Sgshapiro ** already loaded in the cache. 34364565Sgshapiro */ 34464565Sgshapiro 34564565Sgshapiro if (mci->mci_host == NULL && 34664565Sgshapiro (mci->mci_host = s->s_name) != NULL && 34764565Sgshapiro !mci_load_persistent(mci)) 34838032Speter { 34938032Speter if (tTd(42, 2)) 35090795Sgshapiro sm_dprintf("mci_get(%s %s): lock failed\n", 35164565Sgshapiro host, m->m_name); 35238032Speter mci->mci_exitstat = EX_TEMPFAIL; 35338032Speter mci->mci_state = MCIS_CLOSED; 35438032Speter mci->mci_statfile = NULL; 35538032Speter return mci; 35638032Speter } 35738032Speter 35838032Speter if (tTd(42, 2)) 35938032Speter { 36090795Sgshapiro sm_dprintf("mci_get(%s %s): mci_state=%d, _flags=%lx, _exitstat=%d, _errno=%d\n", 36138032Speter host, m->m_name, mci->mci_state, mci->mci_flags, 36238032Speter mci->mci_exitstat, mci->mci_errno); 36338032Speter } 36438032Speter 36538032Speter if (mci->mci_state == MCIS_OPEN) 36638032Speter { 36738032Speter /* poke the connection to see if it's still alive */ 36838032Speter (void) smtpprobe(mci); 36938032Speter 37038032Speter /* reset the stored state in the event of a timeout */ 37138032Speter if (mci->mci_state != MCIS_OPEN) 37238032Speter { 37338032Speter mci->mci_errno = 0; 37438032Speter mci->mci_exitstat = EX_OK; 37538032Speter mci->mci_state = MCIS_CLOSED; 37638032Speter } 37738032Speter else 37838032Speter { 37990795Sgshapiro /* get peer host address */ 38038032Speter /* (this should really be in the mci struct) */ 38138032Speter SOCKADDR_LEN_T socklen = sizeof CurHostAddr; 38238032Speter 38390795Sgshapiro (void) getpeername(sm_io_getinfo(mci->mci_in, 38490795Sgshapiro SM_IO_WHAT_FD, NULL), 38538032Speter (struct sockaddr *) &CurHostAddr, &socklen); 38638032Speter } 38738032Speter } 38838032Speter if (mci->mci_state == MCIS_CLOSED) 38938032Speter { 39038032Speter time_t now = curtime(); 39138032Speter 39238032Speter /* if this info is stale, ignore it */ 39394337Sgshapiro if (mci->mci_lastuse + MciInfoTimeout <= now) 39438032Speter { 39538032Speter mci->mci_lastuse = now; 39638032Speter mci->mci_errno = 0; 39738032Speter mci->mci_exitstat = EX_OK; 39838032Speter } 39938032Speter } 40038032Speter 40138032Speter return mci; 40238032Speter} 40390795Sgshapiro/* 40490795Sgshapiro** MCI_NEW -- allocate new MCI structure 40590795Sgshapiro** 40690795Sgshapiro** Parameters: 40790795Sgshapiro** rpool -- if non-NULL: allocate from that rpool. 40890795Sgshapiro** 40990795Sgshapiro** Returns: 41090795Sgshapiro** mci (new). 41190795Sgshapiro*/ 41290795Sgshapiro 41390795SgshapiroMCI * 41490795Sgshapiromci_new(rpool) 41590795Sgshapiro SM_RPOOL_T *rpool; 41690795Sgshapiro{ 41790795Sgshapiro register MCI *mci; 41890795Sgshapiro 41990795Sgshapiro if (rpool == NULL) 42090795Sgshapiro mci = (MCI *) sm_malloc_x(sizeof *mci); 42190795Sgshapiro else 42290795Sgshapiro mci = (MCI *) sm_rpool_malloc_x(rpool, sizeof *mci); 42390795Sgshapiro memset((char *) mci, '\0', sizeof *mci); 42490795Sgshapiro mci->mci_rpool = sm_rpool_new_x(NULL); 42590795Sgshapiro mci->mci_macro.mac_rpool = mci->mci_rpool; 42690795Sgshapiro return mci; 42790795Sgshapiro} 42890795Sgshapiro/* 42964565Sgshapiro** MCI_MATCH -- check connection cache for a particular host 43090795Sgshapiro** 43190795Sgshapiro** Parameters: 43290795Sgshapiro** host -- host to look for. 43390795Sgshapiro** m -- mailer. 43490795Sgshapiro** 43590795Sgshapiro** Returns: 43690795Sgshapiro** true iff open connection exists. 43764565Sgshapiro*/ 43864565Sgshapiro 43964565Sgshapirobool 44064565Sgshapiromci_match(host, m) 44164565Sgshapiro char *host; 44264565Sgshapiro MAILER *m; 44364565Sgshapiro{ 44464565Sgshapiro register MCI *mci; 44564565Sgshapiro register STAB *s; 44664565Sgshapiro 44771348Sgshapiro if (m->m_mno < 0 || m->m_mno > MAXMAILERS) 44890795Sgshapiro return false; 44964565Sgshapiro s = stab(host, ST_MCI + m->m_mno, ST_FIND); 45064565Sgshapiro if (s == NULL) 45190795Sgshapiro return false; 45264565Sgshapiro 45364565Sgshapiro mci = &s->s_mci; 45490795Sgshapiro return mci->mci_state == MCIS_OPEN; 45564565Sgshapiro} 45690795Sgshapiro/* 45738032Speter** MCI_SETSTAT -- set status codes in MCI structure. 45838032Speter** 45938032Speter** Parameters: 46038032Speter** mci -- the MCI structure to set. 46138032Speter** xstat -- the exit status code. 46238032Speter** dstat -- the DSN status code. 46338032Speter** rstat -- the SMTP status code. 46438032Speter** 46538032Speter** Returns: 46638032Speter** none. 46738032Speter*/ 46838032Speter 46938032Spetervoid 47038032Spetermci_setstat(mci, xstat, dstat, rstat) 47138032Speter MCI *mci; 47238032Speter int xstat; 47338032Speter char *dstat; 47438032Speter char *rstat; 47538032Speter{ 47638032Speter /* protocol errors should never be interpreted as sticky */ 47738032Speter if (xstat != EX_NOTSTICKY && xstat != EX_PROTOCOL) 47838032Speter mci->mci_exitstat = xstat; 47938032Speter 48090795Sgshapiro SM_FREE_CLR(mci->mci_status); 48190795Sgshapiro if (dstat != NULL) 48290795Sgshapiro mci->mci_status = sm_strdup_x(dstat); 48390795Sgshapiro 48490795Sgshapiro SM_FREE_CLR(mci->mci_rstatus); 48538032Speter if (rstat != NULL) 48690795Sgshapiro mci->mci_rstatus = sm_strdup_x(rstat); 48738032Speter} 48890795Sgshapiro/* 48938032Speter** MCI_DUMP -- dump the contents of an MCI structure. 49038032Speter** 49138032Speter** Parameters: 49238032Speter** mci -- the MCI structure to dump. 49338032Speter** 49438032Speter** Returns: 49538032Speter** none. 49638032Speter** 49738032Speter** Side Effects: 49838032Speter** none. 49938032Speter*/ 50038032Speter 50138032Speterstruct mcifbits 50238032Speter{ 50338032Speter int mcif_bit; /* flag bit */ 50438032Speter char *mcif_name; /* flag name */ 50538032Speter}; 50664565Sgshapirostatic struct mcifbits MciFlags[] = 50738032Speter{ 50838032Speter { MCIF_VALID, "VALID" }, 50938032Speter { MCIF_CACHED, "CACHED" }, 51038032Speter { MCIF_ESMTP, "ESMTP" }, 51138032Speter { MCIF_EXPN, "EXPN" }, 51238032Speter { MCIF_SIZE, "SIZE" }, 51338032Speter { MCIF_8BITMIME, "8BITMIME" }, 51438032Speter { MCIF_7BIT, "7BIT" }, 51538032Speter { MCIF_INHEADER, "INHEADER" }, 51638032Speter { MCIF_CVT8TO7, "CVT8TO7" }, 51738032Speter { MCIF_DSN, "DSN" }, 51838032Speter { MCIF_8BITOK, "8BITOK" }, 51938032Speter { MCIF_CVT7TO8, "CVT7TO8" }, 52038032Speter { MCIF_INMIME, "INMIME" }, 52190795Sgshapiro { MCIF_AUTH, "AUTH" }, 52290795Sgshapiro { MCIF_AUTHACT, "AUTHACT" }, 52390795Sgshapiro { MCIF_ENHSTAT, "ENHSTAT" }, 52490795Sgshapiro { MCIF_PIPELINED, "PIPELINED" }, 52590795Sgshapiro#if STARTTLS 52690795Sgshapiro { MCIF_TLS, "TLS" }, 52790795Sgshapiro { MCIF_TLSACT, "TLSACT" }, 52890795Sgshapiro#endif /* STARTTLS */ 52990795Sgshapiro { MCIF_DLVR_BY, "DLVR_BY" }, 53071348Sgshapiro { 0, NULL } 53138032Speter}; 53238032Speter 53338032Spetervoid 53438032Spetermci_dump(mci, logit) 53538032Speter register MCI *mci; 53638032Speter bool logit; 53738032Speter{ 53838032Speter register char *p; 53938032Speter char *sep; 54038032Speter char buf[4000]; 54138032Speter 54238032Speter sep = logit ? " " : "\n\t"; 54338032Speter p = buf; 54490795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "MCI@%p: ", mci); 54538032Speter p += strlen(p); 54638032Speter if (mci == NULL) 54738032Speter { 54890795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "NULL"); 54938032Speter goto printit; 55038032Speter } 55190795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "flags=%lx", mci->mci_flags); 55238032Speter p += strlen(p); 55338032Speter if (mci->mci_flags != 0) 55438032Speter { 55538032Speter struct mcifbits *f; 55638032Speter 55738032Speter *p++ = '<'; 55838032Speter for (f = MciFlags; f->mcif_bit != 0; f++) 55938032Speter { 56038032Speter if (!bitset(f->mcif_bit, mci->mci_flags)) 56138032Speter continue; 56290795Sgshapiro (void) sm_strlcpyn(p, SPACELEFT(buf, p), 2, 56390795Sgshapiro f->mcif_name, ","); 56438032Speter p += strlen(p); 56538032Speter } 56638032Speter p[-1] = '>'; 56738032Speter } 56890795Sgshapiro 56990795Sgshapiro /* Note: sm_snprintf() takes care of NULL arguments for %s */ 57090795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 57138032Speter ",%serrno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s", 57238032Speter sep, mci->mci_errno, mci->mci_herrno, 57364565Sgshapiro mci->mci_exitstat, mci->mci_state, (int) mci->mci_pid, sep); 57438032Speter p += strlen(p); 57590795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 57638032Speter "maxsize=%ld, phase=%s, mailer=%s,%s", 57790795Sgshapiro mci->mci_maxsize, mci->mci_phase, 57838032Speter mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name, 57938032Speter sep); 58038032Speter p += strlen(p); 58190795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 58238032Speter "status=%s, rstatus=%s,%s", 58390795Sgshapiro mci->mci_status, mci->mci_rstatus, sep); 58438032Speter p += strlen(p); 58590795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 58638032Speter "host=%s, lastuse=%s", 58790795Sgshapiro mci->mci_host, ctime(&mci->mci_lastuse)); 58838032Speterprintit: 58938032Speter if (logit) 59038032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "%.1000s", buf); 59138032Speter else 59290795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", buf); 59338032Speter} 59490795Sgshapiro/* 59538032Speter** MCI_DUMP_ALL -- print the entire MCI cache 59638032Speter** 59738032Speter** Parameters: 59838032Speter** logit -- if set, log the result instead of printing 59938032Speter** to stdout. 60038032Speter** 60138032Speter** Returns: 60238032Speter** none. 60338032Speter*/ 60438032Speter 60538032Spetervoid 60638032Spetermci_dump_all(logit) 60738032Speter bool logit; 60838032Speter{ 60938032Speter register int i; 61038032Speter 61138032Speter if (MciCache == NULL) 61238032Speter return; 61338032Speter 61438032Speter for (i = 0; i < MaxMciCache; i++) 61538032Speter mci_dump(MciCache[i], logit); 61638032Speter} 61790795Sgshapiro/* 61842580Speter** MCI_LOCK_HOST -- Lock host while sending. 61938032Speter** 62038032Speter** If we are contacting a host, we'll need to 62138032Speter** update the status information in the host status 62238032Speter** file, and if we want to do that, we ought to have 62338032Speter** locked it. This has the (according to some) 62438032Speter** desirable effect of serializing connectivity with 62590795Sgshapiro** remote hosts -- i.e.: one connection to a given 62638032Speter** host at a time. 62738032Speter** 62838032Speter** Parameters: 62938032Speter** mci -- containing the host we want to lock. 63038032Speter** 63138032Speter** Returns: 63264565Sgshapiro** EX_OK -- got the lock. 63338032Speter** EX_TEMPFAIL -- didn't get the lock. 63438032Speter*/ 63538032Speter 63638032Speterint 63738032Spetermci_lock_host(mci) 63838032Speter MCI *mci; 63938032Speter{ 64038032Speter if (mci == NULL) 64138032Speter { 64238032Speter if (tTd(56, 1)) 64390795Sgshapiro sm_dprintf("mci_lock_host: NULL mci\n"); 64438032Speter return EX_OK; 64538032Speter } 64638032Speter 64738032Speter if (!SingleThreadDelivery) 64838032Speter return EX_OK; 64938032Speter 65038032Speter return mci_lock_host_statfile(mci); 65138032Speter} 65238032Speter 65364565Sgshapirostatic int 65438032Spetermci_lock_host_statfile(mci) 65538032Speter MCI *mci; 65638032Speter{ 65764565Sgshapiro int save_errno = errno; 65838032Speter int retVal = EX_OK; 65998125Sgshapiro char fname[MAXPATHLEN]; 66038032Speter 66138032Speter if (HostStatDir == NULL || mci->mci_host == NULL) 66238032Speter return EX_OK; 66338032Speter 66438032Speter if (tTd(56, 2)) 66590795Sgshapiro sm_dprintf("mci_lock_host: attempting to lock %s\n", 66690795Sgshapiro mci->mci_host); 66738032Speter 66890795Sgshapiro if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname, 66990795Sgshapiro true) < 0) 67038032Speter { 67138032Speter /* of course this should never happen */ 67238032Speter if (tTd(56, 2)) 67390795Sgshapiro sm_dprintf("mci_lock_host: Failed to generate host path for %s\n", 67490795Sgshapiro mci->mci_host); 67538032Speter 67638032Speter retVal = EX_TEMPFAIL; 67738032Speter goto cleanup; 67838032Speter } 67938032Speter 68038032Speter mci->mci_statfile = safefopen(fname, O_RDWR, FileMode, 68164565Sgshapiro SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH|SFF_CREAT); 68238032Speter 68338032Speter if (mci->mci_statfile == NULL) 68438032Speter { 68590795Sgshapiro syserr("mci_lock_host: cannot create host lock file %s", fname); 68638032Speter goto cleanup; 68738032Speter } 68838032Speter 68990795Sgshapiro if (!lockfile(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL), 69090795Sgshapiro fname, "", LOCK_EX|LOCK_NB)) 69138032Speter { 69238032Speter if (tTd(56, 2)) 69390795Sgshapiro sm_dprintf("mci_lock_host: couldn't get lock on %s\n", 69464565Sgshapiro fname); 69590795Sgshapiro (void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT); 69638032Speter mci->mci_statfile = NULL; 69738032Speter retVal = EX_TEMPFAIL; 69838032Speter goto cleanup; 69938032Speter } 70038032Speter 70138032Speter if (tTd(56, 12) && mci->mci_statfile != NULL) 70290795Sgshapiro sm_dprintf("mci_lock_host: Sanity check -- lock is good\n"); 70338032Speter 70438032Spetercleanup: 70564565Sgshapiro errno = save_errno; 70638032Speter return retVal; 70738032Speter} 70890795Sgshapiro/* 70938032Speter** MCI_UNLOCK_HOST -- unlock host 71038032Speter** 71138032Speter** Clean up the lock on a host, close the file, let 71238032Speter** someone else use it. 71338032Speter** 71438032Speter** Parameters: 71538032Speter** mci -- us. 71638032Speter** 71738032Speter** Returns: 71838032Speter** nothing. 71938032Speter*/ 72038032Speter 72138032Spetervoid 72238032Spetermci_unlock_host(mci) 72338032Speter MCI *mci; 72438032Speter{ 72564565Sgshapiro int save_errno = errno; 72638032Speter 72738032Speter if (mci == NULL) 72838032Speter { 72938032Speter if (tTd(56, 1)) 73090795Sgshapiro sm_dprintf("mci_unlock_host: NULL mci\n"); 73138032Speter return; 73238032Speter } 73338032Speter 73438032Speter if (HostStatDir == NULL || mci->mci_host == NULL) 73538032Speter return; 73638032Speter 73738032Speter if (!SingleThreadDelivery && mci_lock_host_statfile(mci) == EX_TEMPFAIL) 73838032Speter { 73938032Speter if (tTd(56, 1)) 74090795Sgshapiro sm_dprintf("mci_unlock_host: stat file already locked\n"); 74138032Speter } 74238032Speter else 74338032Speter { 74438032Speter if (tTd(56, 2)) 74590795Sgshapiro sm_dprintf("mci_unlock_host: store prior to unlock\n"); 74638032Speter mci_store_persistent(mci); 74738032Speter } 74838032Speter 74938032Speter if (mci->mci_statfile != NULL) 75038032Speter { 75190795Sgshapiro (void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT); 75238032Speter mci->mci_statfile = NULL; 75338032Speter } 75438032Speter 75564565Sgshapiro errno = save_errno; 75638032Speter} 75790795Sgshapiro/* 75842580Speter** MCI_LOAD_PERSISTENT -- load persistent host info 75938032Speter** 76038032Speter** Load information about host that is kept 76138032Speter** in common for all running sendmails. 76238032Speter** 76338032Speter** Parameters: 76490795Sgshapiro** mci -- the host/connection to load persistent info for. 76538032Speter** 76638032Speter** Returns: 76790795Sgshapiro** true -- lock was successful 76890795Sgshapiro** false -- lock failed 76938032Speter*/ 77038032Speter 77164565Sgshapirostatic bool 77238032Spetermci_load_persistent(mci) 77338032Speter MCI *mci; 77438032Speter{ 77564565Sgshapiro int save_errno = errno; 77690795Sgshapiro bool locked = true; 77790795Sgshapiro SM_FILE_T *fp; 77898125Sgshapiro char fname[MAXPATHLEN]; 77938032Speter 78038032Speter if (mci == NULL) 78138032Speter { 78238032Speter if (tTd(56, 1)) 78390795Sgshapiro sm_dprintf("mci_load_persistent: NULL mci\n"); 78490795Sgshapiro return true; 78538032Speter } 78638032Speter 78738032Speter if (IgnoreHostStatus || HostStatDir == NULL || mci->mci_host == NULL) 78890795Sgshapiro return true; 78964565Sgshapiro 79038032Speter /* Already have the persistent information in memory */ 79138032Speter if (SingleThreadDelivery && mci->mci_statfile != NULL) 79290795Sgshapiro return true; 79338032Speter 79438032Speter if (tTd(56, 1)) 79590795Sgshapiro sm_dprintf("mci_load_persistent: Attempting to load persistent information for %s\n", 79690795Sgshapiro mci->mci_host); 79764565Sgshapiro 79890795Sgshapiro if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname, 79990795Sgshapiro false) < 0) 80038032Speter { 80138032Speter /* Not much we can do if the file isn't there... */ 80238032Speter if (tTd(56, 1)) 80390795Sgshapiro sm_dprintf("mci_load_persistent: Couldn't generate host path\n"); 80438032Speter goto cleanup; 80538032Speter } 80638032Speter 80738032Speter fp = safefopen(fname, O_RDONLY, FileMode, 80838032Speter SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH); 80938032Speter if (fp == NULL) 81038032Speter { 81138032Speter /* I can't think of any reason this should ever happen */ 81238032Speter if (tTd(56, 1)) 81390795Sgshapiro sm_dprintf("mci_load_persistent: open(%s): %s\n", 81490795Sgshapiro fname, sm_errstring(errno)); 81538032Speter goto cleanup; 81638032Speter } 81738032Speter 81838032Speter FileName = fname; 81990795Sgshapiro locked = lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname, "", 82090795Sgshapiro LOCK_SH|LOCK_NB); 82164565Sgshapiro if (locked) 82264565Sgshapiro { 82364565Sgshapiro (void) mci_read_persistent(fp, mci); 82490795Sgshapiro (void) lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname, 82590795Sgshapiro "", LOCK_UN); 82664565Sgshapiro } 82738032Speter FileName = NULL; 82890795Sgshapiro (void) sm_io_close(fp, SM_TIME_DEFAULT); 82938032Speter 83038032Spetercleanup: 83164565Sgshapiro errno = save_errno; 83238032Speter return locked; 83338032Speter} 83490795Sgshapiro/* 83538032Speter** MCI_READ_PERSISTENT -- read persistent host status file 83638032Speter** 83738032Speter** Parameters: 83838032Speter** fp -- the file pointer to read. 83938032Speter** mci -- the pointer to fill in. 84038032Speter** 84138032Speter** Returns: 84238032Speter** -1 -- if the file was corrupt. 84338032Speter** 0 -- otherwise. 84438032Speter** 84538032Speter** Warning: 84638032Speter** This code makes the assumption that this data 84738032Speter** will be read in an atomic fashion, and that the data 84838032Speter** was written in an atomic fashion. Any other functioning 84938032Speter** may lead to some form of insanity. This should be 85038032Speter** perfectly safe due to underlying stdio buffering. 85138032Speter*/ 85238032Speter 85364565Sgshapirostatic int 85438032Spetermci_read_persistent(fp, mci) 85590795Sgshapiro SM_FILE_T *fp; 85638032Speter register MCI *mci; 85738032Speter{ 85838032Speter int ver; 85938032Speter register char *p; 86038032Speter int saveLineNumber = LineNumber; 86138032Speter char buf[MAXLINE]; 86238032Speter 86338032Speter if (fp == NULL) 86438032Speter syserr("mci_read_persistent: NULL fp"); 86538032Speter if (mci == NULL) 86638032Speter syserr("mci_read_persistent: NULL mci"); 86738032Speter if (tTd(56, 93)) 86838032Speter { 86990795Sgshapiro sm_dprintf("mci_read_persistent: fp=%lx, mci=", 87090795Sgshapiro (unsigned long) fp); 87138032Speter } 87238032Speter 87390795Sgshapiro SM_FREE_CLR(mci->mci_status); 87490795Sgshapiro SM_FREE_CLR(mci->mci_rstatus); 87538032Speter 87690795Sgshapiro sm_io_rewind(fp, SM_TIME_DEFAULT); 87738032Speter ver = -1; 87838032Speter LineNumber = 0; 87990795Sgshapiro while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, sizeof buf) != NULL) 88038032Speter { 88138032Speter LineNumber++; 88238032Speter p = strchr(buf, '\n'); 88338032Speter if (p != NULL) 88438032Speter *p = '\0'; 88538032Speter switch (buf[0]) 88638032Speter { 88738032Speter case 'V': /* version stamp */ 88838032Speter ver = atoi(&buf[1]); 88938032Speter if (ver < 0 || ver > 0) 89038032Speter syserr("Unknown host status version %d: %d max", 89138032Speter ver, 0); 89238032Speter break; 89338032Speter 89438032Speter case 'E': /* UNIX error number */ 89538032Speter mci->mci_errno = atoi(&buf[1]); 89638032Speter break; 89738032Speter 89838032Speter case 'H': /* DNS error number */ 89938032Speter mci->mci_herrno = atoi(&buf[1]); 90038032Speter break; 90138032Speter 90238032Speter case 'S': /* UNIX exit status */ 90338032Speter mci->mci_exitstat = atoi(&buf[1]); 90438032Speter break; 90538032Speter 90638032Speter case 'D': /* DSN status */ 90738032Speter mci->mci_status = newstr(&buf[1]); 90838032Speter break; 90938032Speter 91038032Speter case 'R': /* SMTP status */ 91138032Speter mci->mci_rstatus = newstr(&buf[1]); 91238032Speter break; 91338032Speter 91438032Speter case 'U': /* last usage time */ 91538032Speter mci->mci_lastuse = atol(&buf[1]); 91638032Speter break; 91738032Speter 91838032Speter case '.': /* end of file */ 91990795Sgshapiro if (tTd(56, 93)) 92090795Sgshapiro mci_dump(mci, false); 92138032Speter return 0; 92238032Speter 92338032Speter default: 92438032Speter sm_syslog(LOG_CRIT, NOQID, 92538032Speter "%s: line %d: Unknown host status line \"%s\"", 92638032Speter FileName == NULL ? mci->mci_host : FileName, 92738032Speter LineNumber, buf); 92838032Speter LineNumber = saveLineNumber; 92938032Speter return -1; 93038032Speter } 93138032Speter } 93238032Speter LineNumber = saveLineNumber; 93390795Sgshapiro if (tTd(56, 93)) 93490795Sgshapiro sm_dprintf("incomplete (missing dot for EOF)\n"); 93538032Speter if (ver < 0) 93638032Speter return -1; 93738032Speter return 0; 93838032Speter} 93990795Sgshapiro/* 94038032Speter** MCI_STORE_PERSISTENT -- Store persistent MCI information 94138032Speter** 94238032Speter** Store information about host that is kept 94338032Speter** in common for all running sendmails. 94438032Speter** 94538032Speter** Parameters: 94638032Speter** mci -- the host/connection to store persistent info for. 94738032Speter** 94838032Speter** Returns: 94938032Speter** none. 95038032Speter*/ 95138032Speter 95238032Spetervoid 95338032Spetermci_store_persistent(mci) 95438032Speter MCI *mci; 95538032Speter{ 95664565Sgshapiro int save_errno = errno; 95738032Speter 95838032Speter if (mci == NULL) 95938032Speter { 96038032Speter if (tTd(56, 1)) 96190795Sgshapiro sm_dprintf("mci_store_persistent: NULL mci\n"); 96238032Speter return; 96338032Speter } 96438032Speter 96538032Speter if (HostStatDir == NULL || mci->mci_host == NULL) 96638032Speter return; 96738032Speter 96838032Speter if (tTd(56, 1)) 96990795Sgshapiro sm_dprintf("mci_store_persistent: Storing information for %s\n", 97090795Sgshapiro mci->mci_host); 97138032Speter 97238032Speter if (mci->mci_statfile == NULL) 97338032Speter { 97438032Speter if (tTd(56, 1)) 97590795Sgshapiro sm_dprintf("mci_store_persistent: no statfile\n"); 97638032Speter return; 97738032Speter } 97838032Speter 97990795Sgshapiro sm_io_rewind(mci->mci_statfile, SM_TIME_DEFAULT); 98038032Speter#if !NOFTRUNCATE 98190795Sgshapiro (void) ftruncate(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL), 98290795Sgshapiro (off_t) 0); 98364565Sgshapiro#endif /* !NOFTRUNCATE */ 98438032Speter 98590795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "V0\n"); 98690795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "E%d\n", 98790795Sgshapiro mci->mci_errno); 98890795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "H%d\n", 98990795Sgshapiro mci->mci_herrno); 99090795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "S%d\n", 99190795Sgshapiro mci->mci_exitstat); 99238032Speter if (mci->mci_status != NULL) 99390795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, 99490795Sgshapiro "D%.80s\n", 99590795Sgshapiro denlstring(mci->mci_status, true, false)); 99638032Speter if (mci->mci_rstatus != NULL) 99790795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, 99890795Sgshapiro "R%.80s\n", 99990795Sgshapiro denlstring(mci->mci_rstatus, true, false)); 100090795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "U%ld\n", 100190795Sgshapiro (long)(mci->mci_lastuse)); 100290795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, ".\n"); 100338032Speter 100490795Sgshapiro (void) sm_io_flush(mci->mci_statfile, SM_TIME_DEFAULT); 100538032Speter 100664565Sgshapiro errno = save_errno; 100738032Speter return; 100838032Speter} 100990795Sgshapiro/* 101038032Speter** MCI_TRAVERSE_PERSISTENT -- walk persistent status tree 101138032Speter** 101238032Speter** Recursively find all the mci host files in `pathname'. Default to 101338032Speter** main host status directory if no path is provided. 101438032Speter** Call (*action)(pathname, host) for each file found. 101538032Speter** 101638032Speter** Note: all information is collected in a list before it is processed. 101738032Speter** This may not be the best way to do it, but it seems safest, since 101838032Speter** the file system would be touched while we are attempting to traverse 101938032Speter** the directory tree otherwise (during purges). 102038032Speter** 102138032Speter** Parameters: 102238032Speter** action -- function to call on each node. If returns < 0, 102338032Speter** return immediately. 102438032Speter** pathname -- root of tree. If null, use main host status 102538032Speter** directory. 102638032Speter** 102738032Speter** Returns: 102838032Speter** < 0 -- if any action routine returns a negative value, that 102938032Speter** value is returned. 103038032Speter** 0 -- if we successfully went to completion. 103164565Sgshapiro** > 0 -- return status from action() 103238032Speter*/ 103338032Speter 103438032Speterint 103538032Spetermci_traverse_persistent(action, pathname) 103638032Speter int (*action)(); 103738032Speter char *pathname; 103838032Speter{ 103938032Speter struct stat statbuf; 104038032Speter DIR *d; 104138032Speter int ret; 104238032Speter 104338032Speter if (pathname == NULL) 104438032Speter pathname = HostStatDir; 104538032Speter if (pathname == NULL) 104638032Speter return -1; 104738032Speter 104838032Speter if (tTd(56, 1)) 104990795Sgshapiro sm_dprintf("mci_traverse: pathname is %s\n", pathname); 105038032Speter 105138032Speter ret = stat(pathname, &statbuf); 105238032Speter if (ret < 0) 105338032Speter { 105438032Speter if (tTd(56, 2)) 105590795Sgshapiro sm_dprintf("mci_traverse: Failed to stat %s: %s\n", 105690795Sgshapiro pathname, sm_errstring(errno)); 105738032Speter return ret; 105838032Speter } 105938032Speter if (S_ISDIR(statbuf.st_mode)) 106038032Speter { 106190795Sgshapiro bool leftone, removedone; 106290795Sgshapiro size_t len; 106390795Sgshapiro char *newptr; 106438032Speter struct dirent *e; 106598125Sgshapiro char newpath[MAXPATHLEN]; 106638032Speter 106738032Speter if ((d = opendir(pathname)) == NULL) 106838032Speter { 106938032Speter if (tTd(56, 2)) 107090795Sgshapiro sm_dprintf("mci_traverse: opendir %s: %s\n", 107190795Sgshapiro pathname, sm_errstring(errno)); 107238032Speter return -1; 107338032Speter } 107490795Sgshapiro len = sizeof(newpath) - MAXNAMLEN - 3; 107590795Sgshapiro if (sm_strlcpy(newpath, pathname, len) >= len) 107638032Speter { 107738032Speter if (tTd(56, 2)) 107890795Sgshapiro sm_dprintf("mci_traverse: path \"%s\" too long", 107938032Speter pathname); 108038032Speter return -1; 108138032Speter } 108238032Speter newptr = newpath + strlen(newpath); 108338032Speter *newptr++ = '/'; 108438032Speter 108564565Sgshapiro /* 108664565Sgshapiro ** repeat until no file has been removed 108764565Sgshapiro ** this may become ugly when several files "expire" 108864565Sgshapiro ** during these loops, but it's better than doing 108964565Sgshapiro ** a rewinddir() inside the inner loop 109064565Sgshapiro */ 109190795Sgshapiro 109264565Sgshapiro do 109338032Speter { 109490795Sgshapiro leftone = removedone = false; 109564565Sgshapiro while ((e = readdir(d)) != NULL) 109664565Sgshapiro { 109764565Sgshapiro if (e->d_name[0] == '.') 109864565Sgshapiro continue; 109938032Speter 110090795Sgshapiro (void) sm_strlcpy(newptr, e->d_name, 110164565Sgshapiro sizeof newpath - 110264565Sgshapiro (newptr - newpath)); 110338032Speter 110477352Sgshapiro if (StopRequest) 110577352Sgshapiro stop_sendmail(); 110664565Sgshapiro ret = mci_traverse_persistent(action, newpath); 110764565Sgshapiro if (ret < 0) 110864565Sgshapiro break; 110964565Sgshapiro if (ret == 1) 111090795Sgshapiro leftone = true; 111164565Sgshapiro if (!removedone && ret == 0 && 111264565Sgshapiro action == mci_purge_persistent) 111390795Sgshapiro removedone = true; 111464565Sgshapiro } 111538032Speter if (ret < 0) 111638032Speter break; 111790795Sgshapiro 111838032Speter /* 111938032Speter ** The following appears to be 112038032Speter ** necessary during purges, since 112138032Speter ** we modify the directory structure 112238032Speter */ 112390795Sgshapiro 112464565Sgshapiro if (removedone) 112538032Speter rewinddir(d); 112664565Sgshapiro if (tTd(56, 40)) 112790795Sgshapiro sm_dprintf("mci_traverse: path %s: ret %d removed %d left %d\n", 112864565Sgshapiro pathname, ret, removedone, leftone); 112964565Sgshapiro } while (removedone); 113038032Speter 113138032Speter /* purge (or whatever) the directory proper */ 113264565Sgshapiro if (!leftone) 113364565Sgshapiro { 113464565Sgshapiro *--newptr = '\0'; 113564565Sgshapiro ret = (*action)(newpath, NULL); 113664565Sgshapiro } 113764565Sgshapiro (void) closedir(d); 113838032Speter } 113938032Speter else if (S_ISREG(statbuf.st_mode)) 114038032Speter { 114138032Speter char *end = pathname + strlen(pathname) - 1; 114238032Speter char *start; 114338032Speter char *scan; 114438032Speter char host[MAXHOSTNAMELEN]; 114538032Speter char *hostptr = host; 114664565Sgshapiro 114738032Speter /* 114838032Speter ** Reconstruct the host name from the path to the 114938032Speter ** persistent information. 115038032Speter */ 115138032Speter 115238032Speter do 115338032Speter { 115438032Speter if (hostptr != host) 115538032Speter *(hostptr++) = '.'; 115638032Speter start = end; 115738032Speter while (*(start - 1) != '/') 115838032Speter start--; 115938032Speter 116038032Speter if (*end == '.') 116138032Speter end--; 116238032Speter 116338032Speter for (scan = start; scan <= end; scan++) 116438032Speter *(hostptr++) = *scan; 116538032Speter 116638032Speter end = start - 2; 116738032Speter } while (*end == '.'); 116838032Speter 116938032Speter *hostptr = '\0'; 117038032Speter 117138032Speter /* 117242580Speter ** Do something with the file containing the persistent 117342580Speter ** information. 117438032Speter */ 117590795Sgshapiro 117638032Speter ret = (*action)(pathname, host); 117738032Speter } 117838032Speter 117938032Speter return ret; 118038032Speter} 118190795Sgshapiro/* 118264565Sgshapiro** MCI_PRINT_PERSISTENT -- print persistent info 118338032Speter** 118438032Speter** Dump the persistent information in the file 'pathname' 118538032Speter** 118638032Speter** Parameters: 118738032Speter** pathname -- the pathname to the status file. 118838032Speter** hostname -- the corresponding host name. 118938032Speter** 119038032Speter** Returns: 119138032Speter** 0 119238032Speter*/ 119338032Speter 119438032Speterint 119538032Spetermci_print_persistent(pathname, hostname) 119638032Speter char *pathname; 119738032Speter char *hostname; 119838032Speter{ 119990795Sgshapiro static bool initflag = false; 120090795Sgshapiro SM_FILE_T *fp; 120138032Speter int width = Verbose ? 78 : 25; 120238032Speter bool locked; 120338032Speter MCI mcib; 120438032Speter 120538032Speter /* skip directories */ 120638032Speter if (hostname == NULL) 120738032Speter return 0; 120838032Speter 120977352Sgshapiro if (StopRequest) 121077352Sgshapiro stop_sendmail(); 121177352Sgshapiro 121238032Speter if (!initflag) 121338032Speter { 121490795Sgshapiro initflag = true; 121590795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 121690795Sgshapiro " -------------- Hostname --------------- How long ago ---------Results---------\n"); 121738032Speter } 121838032Speter 121990795Sgshapiro fp = safefopen(pathname, O_RDONLY, FileMode, 122038032Speter SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH); 122138032Speter 122238032Speter if (fp == NULL) 122338032Speter { 122438032Speter if (tTd(56, 1)) 122590795Sgshapiro sm_dprintf("mci_print_persistent: cannot open %s: %s\n", 122690795Sgshapiro pathname, sm_errstring(errno)); 122738032Speter return 0; 122838032Speter } 122938032Speter 123038032Speter FileName = pathname; 123164565Sgshapiro memset(&mcib, '\0', sizeof mcib); 123238032Speter if (mci_read_persistent(fp, &mcib) < 0) 123338032Speter { 123438032Speter syserr("%s: could not read status file", pathname); 123590795Sgshapiro (void) sm_io_close(fp, SM_TIME_DEFAULT); 123638032Speter FileName = NULL; 123738032Speter return 0; 123838032Speter } 123938032Speter 124090795Sgshapiro locked = !lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), pathname, 124190795Sgshapiro "", LOCK_SH|LOCK_NB); 124290795Sgshapiro (void) sm_io_close(fp, SM_TIME_DEFAULT); 124338032Speter FileName = NULL; 124438032Speter 124590795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%c%-39s %12s ", 124690795Sgshapiro locked ? '*' : ' ', hostname, 124790795Sgshapiro pintvl(curtime() - mcib.mci_lastuse, true)); 124838032Speter if (mcib.mci_rstatus != NULL) 124990795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", width, 125090795Sgshapiro mcib.mci_rstatus); 125138032Speter else if (mcib.mci_exitstat == EX_TEMPFAIL && mcib.mci_errno != 0) 125290795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 125390795Sgshapiro "Deferred: %.*s\n", width - 10, 125490795Sgshapiro sm_errstring(mcib.mci_errno)); 125538032Speter else if (mcib.mci_exitstat != 0) 125638032Speter { 125790795Sgshapiro char *exmsg = sm_sysexmsg(mcib.mci_exitstat); 125838032Speter 125990795Sgshapiro if (exmsg == NULL) 126038032Speter { 126138032Speter char buf[80]; 126238032Speter 126390795Sgshapiro (void) sm_snprintf(buf, sizeof buf, 126490795Sgshapiro "Unknown mailer error %d", 126538032Speter mcib.mci_exitstat); 126690795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", 126790795Sgshapiro width, buf); 126838032Speter } 126938032Speter else 127090795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", 127190795Sgshapiro width, &exmsg[5]); 127238032Speter } 127338032Speter else if (mcib.mci_errno == 0) 127490795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK\n"); 127538032Speter else 127690795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK: %.*s\n", 127790795Sgshapiro width - 4, sm_errstring(mcib.mci_errno)); 127838032Speter 127938032Speter return 0; 128038032Speter} 128190795Sgshapiro/* 128238032Speter** MCI_PURGE_PERSISTENT -- Remove a persistence status file. 128338032Speter** 128438032Speter** Parameters: 128538032Speter** pathname -- path to the status file. 128638032Speter** hostname -- name of host corresponding to that file. 128738032Speter** NULL if this is a directory (domain). 128838032Speter** 128938032Speter** Returns: 129064565Sgshapiro** 0 -- ok 129164565Sgshapiro** 1 -- file not deleted (too young, incorrect format) 129264565Sgshapiro** < 0 -- some error occurred 129338032Speter*/ 129438032Speter 129538032Speterint 129638032Spetermci_purge_persistent(pathname, hostname) 129738032Speter char *pathname; 129838032Speter char *hostname; 129938032Speter{ 130064565Sgshapiro struct stat statbuf; 130138032Speter char *end = pathname + strlen(pathname) - 1; 130264565Sgshapiro int ret; 130338032Speter 130438032Speter if (tTd(56, 1)) 130590795Sgshapiro sm_dprintf("mci_purge_persistent: purging %s\n", pathname); 130638032Speter 130764565Sgshapiro ret = stat(pathname, &statbuf); 130864565Sgshapiro if (ret < 0) 130964565Sgshapiro { 131064565Sgshapiro if (tTd(56, 2)) 131190795Sgshapiro sm_dprintf("mci_purge_persistent: Failed to stat %s: %s\n", 131290795Sgshapiro pathname, sm_errstring(errno)); 131364565Sgshapiro return ret; 131464565Sgshapiro } 131594337Sgshapiro if (curtime() - statbuf.st_mtime <= MciInfoTimeout) 131664565Sgshapiro return 1; 131738032Speter if (hostname != NULL) 131838032Speter { 131938032Speter /* remove the file */ 132090795Sgshapiro ret = unlink(pathname); 132190795Sgshapiro if (ret < 0) 132238032Speter { 132390795Sgshapiro if (LogLevel > 8) 132490795Sgshapiro sm_syslog(LOG_ERR, NOQID, 132590795Sgshapiro "mci_purge_persistent: failed to unlink %s: %s", 132690795Sgshapiro pathname, sm_errstring(errno)); 132738032Speter if (tTd(56, 2)) 132890795Sgshapiro sm_dprintf("mci_purge_persistent: failed to unlink %s: %s\n", 132990795Sgshapiro pathname, sm_errstring(errno)); 133090795Sgshapiro return ret; 133138032Speter } 133238032Speter } 133338032Speter else 133438032Speter { 133538032Speter /* remove the directory */ 133638032Speter if (*end != '.') 133764565Sgshapiro return 1; 133838032Speter 133938032Speter if (tTd(56, 1)) 134090795Sgshapiro sm_dprintf("mci_purge_persistent: dpurge %s\n", pathname); 134138032Speter 134290795Sgshapiro ret = rmdir(pathname); 134390795Sgshapiro if (ret < 0) 134438032Speter { 134538032Speter if (tTd(56, 2)) 134690795Sgshapiro sm_dprintf("mci_purge_persistent: rmdir %s: %s\n", 134790795Sgshapiro pathname, sm_errstring(errno)); 134890795Sgshapiro return ret; 134938032Speter } 135038032Speter } 135138032Speter 135238032Speter return 0; 135338032Speter} 135490795Sgshapiro/* 135542580Speter** MCI_GENERATE_PERSISTENT_PATH -- generate path from hostname 135638032Speter** 135738032Speter** Given `host', convert from a.b.c to $QueueDir/.hoststat/c./b./a, 135838032Speter** putting the result into `path'. if `createflag' is set, intervening 135938032Speter** directories will be created as needed. 136038032Speter** 136138032Speter** Parameters: 136238032Speter** host -- host name to convert from. 136338032Speter** path -- place to store result. 136438032Speter** pathlen -- length of path buffer. 136538032Speter** createflag -- if set, create intervening directories as 136638032Speter** needed. 136738032Speter** 136838032Speter** Returns: 136938032Speter** 0 -- success 137038032Speter** -1 -- failure 137138032Speter*/ 137238032Speter 137364565Sgshapirostatic int 137438032Spetermci_generate_persistent_path(host, path, pathlen, createflag) 137538032Speter const char *host; 137638032Speter char *path; 137738032Speter int pathlen; 137838032Speter bool createflag; 137938032Speter{ 138038032Speter char *elem, *p, *x, ch; 138138032Speter int ret = 0; 138238032Speter int len; 138338032Speter char t_host[MAXHOSTNAMELEN]; 138464565Sgshapiro#if NETINET6 138564565Sgshapiro struct in6_addr in6_addr; 138664565Sgshapiro#endif /* NETINET6 */ 138738032Speter 138838032Speter /* 138938032Speter ** Rationality check the arguments. 139038032Speter */ 139138032Speter 139238032Speter if (host == NULL) 139338032Speter { 139438032Speter syserr("mci_generate_persistent_path: null host"); 139538032Speter return -1; 139638032Speter } 139738032Speter if (path == NULL) 139838032Speter { 139938032Speter syserr("mci_generate_persistent_path: null path"); 140038032Speter return -1; 140138032Speter } 140238032Speter 140338032Speter if (tTd(56, 80)) 140490795Sgshapiro sm_dprintf("mci_generate_persistent_path(%s): ", host); 140538032Speter 140638032Speter if (*host == '\0' || *host == '.') 140738032Speter return -1; 140838032Speter 140938032Speter /* make certain this is not a bracketed host number */ 141038032Speter if (strlen(host) > sizeof t_host - 1) 141138032Speter return -1; 141238032Speter if (host[0] == '[') 141390795Sgshapiro (void) sm_strlcpy(t_host, host + 1, sizeof t_host); 141464565Sgshapiro else 141590795Sgshapiro (void) sm_strlcpy(t_host, host, sizeof t_host); 141638032Speter 141738032Speter /* 141838032Speter ** Delete any trailing dots from the hostname. 141938032Speter ** Leave 'elem' pointing at the \0. 142038032Speter */ 142138032Speter 142238032Speter elem = t_host + strlen(t_host); 142338032Speter while (elem > t_host && 142438032Speter (elem[-1] == '.' || (host[0] == '[' && elem[-1] == ']'))) 142538032Speter *--elem = '\0'; 142638032Speter 142738032Speter /* check for bogus bracketed address */ 142890795Sgshapiro if (host[0] == '[') 142990795Sgshapiro { 143090795Sgshapiro bool good = false; 143164565Sgshapiro# if NETINET6 143290795Sgshapiro if (anynet_pton(AF_INET6, t_host, &in6_addr) == 1) 143390795Sgshapiro good = true; 143464565Sgshapiro# endif /* NETINET6 */ 143564565Sgshapiro# if NETINET 143690795Sgshapiro if (inet_addr(t_host) != INADDR_NONE) 143790795Sgshapiro good = true; 143864565Sgshapiro# endif /* NETINET */ 143990795Sgshapiro if (!good) 144090795Sgshapiro return -1; 144190795Sgshapiro } 144238032Speter 144338032Speter /* check for what will be the final length of the path */ 144438032Speter len = strlen(HostStatDir) + 2; 144538032Speter for (p = (char *) t_host; *p != '\0'; p++) 144638032Speter { 144738032Speter if (*p == '.') 144838032Speter len++; 144938032Speter len++; 145038032Speter if (p[0] == '.' && p[1] == '.') 145138032Speter return -1; 145238032Speter } 145338032Speter if (len > pathlen || len < 1) 145438032Speter return -1; 145590795Sgshapiro (void) sm_strlcpy(path, HostStatDir, pathlen); 145638032Speter p = path + strlen(path); 145738032Speter while (elem > t_host) 145838032Speter { 145938032Speter if (!path_is_dir(path, createflag)) 146038032Speter { 146138032Speter ret = -1; 146238032Speter break; 146338032Speter } 146438032Speter elem--; 146538032Speter while (elem >= t_host && *elem != '.') 146638032Speter elem--; 146738032Speter *p++ = '/'; 146838032Speter x = elem + 1; 146938032Speter while ((ch = *x++) != '\0' && ch != '.') 147038032Speter { 147138032Speter if (isascii(ch) && isupper(ch)) 147238032Speter ch = tolower(ch); 147338032Speter if (ch == '/') 147438032Speter ch = ':'; /* / -> : */ 147538032Speter *p++ = ch; 147638032Speter } 147738032Speter if (elem >= t_host) 147838032Speter *p++ = '.'; 147938032Speter *p = '\0'; 148038032Speter } 148138032Speter if (tTd(56, 80)) 148238032Speter { 148338032Speter if (ret < 0) 148490795Sgshapiro sm_dprintf("FAILURE %d\n", ret); 148538032Speter else 148690795Sgshapiro sm_dprintf("SUCCESS %s\n", path); 148738032Speter } 148864565Sgshapiro return ret; 148938032Speter} 1490