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