source: trunk/third/sendmail/src/mci.c @ 12554

Revision 12554, 27.2 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12553, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3 * Copyright (c) 1995-1997 Eric P. Allman.  All rights reserved.
4 * Copyright (c) 1988, 1993
5 *      The Regents of the University of California.  All rights reserved.
6 *
7 * By using this file, you agree to the terms and conditions set
8 * forth in the LICENSE file which can be found at the top level of
9 * the sendmail distribution.
10 *
11 */
12
13#ifndef lint
14static char sccsid[] = "@(#)mci.c       8.83 (Berkeley) 10/13/1998";
15#endif /* not lint */
16
17#include "sendmail.h"
18#include <arpa/inet.h>
19#include <dirent.h>
20
21/*
22**  Mail Connection Information (MCI) Caching Module.
23**
24**      There are actually two separate things cached.  The first is
25**      the set of all open connections -- these are stored in a
26**      (small) list.  The second is stored in the symbol table; it
27**      has the overall status for all hosts, whether or not there
28**      is a connection open currently.
29**
30**      There should never be too many connections open (since this
31**      could flood the socket table), nor should a connection be
32**      allowed to sit idly for too long.
33**
34**      MaxMciCache is the maximum number of open connections that
35**      will be supported.
36**
37**      MciCacheTimeout is the time (in seconds) that a connection
38**      is permitted to survive without activity.
39**
40**      We actually try any cached connections by sending a NOOP
41**      before we use them; if the NOOP fails we close down the
42**      connection and reopen it.  Note that this means that a
43**      server SMTP that doesn't support NOOP will hose the
44**      algorithm -- but that doesn't seem too likely.
45**
46**      The persistent MCI code is donated by Mark Lovell and Paul
47**      Vixie.  It is based on the long term host status code in KJS
48**      written by Paul but has been adapted by Mark to fit into the
49**      MCI structure.
50*/
51
52MCI     **MciCache;             /* the open connection cache */
53
54extern int      mci_generate_persistent_path __P((const char *, char *, int, bool));
55extern bool     mci_load_persistent __P((MCI *));
56extern void     mci_uncache __P((MCI **, bool));
57/*
58**  MCI_CACHE -- enter a connection structure into the open connection cache
59**
60**      This may cause something else to be flushed.
61**
62**      Parameters:
63**              mci -- the connection to cache.
64**
65**      Returns:
66**              none.
67*/
68
69void
70mci_cache(mci)
71        register MCI *mci;
72{
73        register MCI **mcislot;
74
75        /*
76        **  Find the best slot.  This may cause expired connections
77        **  to be closed.
78        */
79
80        mcislot = mci_scan(mci);
81        if (mcislot == NULL)
82        {
83                /* we don't support caching */
84                return;
85        }
86
87        if (mci->mci_host == NULL)
88                return;
89
90        /* if this is already cached, we are done */
91        if (bitset(MCIF_CACHED, mci->mci_flags))
92                return;
93
94        /* otherwise we may have to clear the slot */
95        if (*mcislot != NULL)
96                mci_uncache(mcislot, TRUE);
97
98        if (tTd(42, 5))
99                printf("mci_cache: caching %lx (%s) in slot %d\n",
100                        (u_long) mci, mci->mci_host, (int)(mcislot - MciCache));
101        if (tTd(91, 100))
102                sm_syslog(LOG_DEBUG, CurEnv->e_id,
103                        "mci_cache: caching %x (%.100s) in slot %d",
104                        mci, mci->mci_host, mcislot - MciCache);
105
106        *mcislot = mci;
107        mci->mci_flags |= MCIF_CACHED;
108}
109/*
110**  MCI_SCAN -- scan the cache, flush junk, and return best slot
111**
112**      Parameters:
113**              savemci -- never flush this one.  Can be null.
114**
115**      Returns:
116**              The LRU (or empty) slot.
117*/
118
119MCI **
120mci_scan(savemci)
121        MCI *savemci;
122{
123        time_t now;
124        register MCI **bestmci;
125        register MCI *mci;
126        register int i;
127
128        if (MaxMciCache <= 0)
129        {
130                /* we don't support caching */
131                return NULL;
132        }
133
134        if (MciCache == NULL)
135        {
136                /* first call */
137                MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache);
138                bzero((char *) MciCache, MaxMciCache * sizeof *MciCache);
139                return (&MciCache[0]);
140        }
141
142        now = curtime();
143        bestmci = &MciCache[0];
144        for (i = 0; i < MaxMciCache; i++)
145        {
146                mci = MciCache[i];
147                if (mci == NULL || mci->mci_state == MCIS_CLOSED)
148                {
149                        bestmci = &MciCache[i];
150                        continue;
151                }
152                if (mci->mci_lastuse + MciCacheTimeout < now && mci != savemci)
153                {
154                        /* connection idle too long -- close it */
155                        bestmci = &MciCache[i];
156                        mci_uncache(bestmci, TRUE);
157                        continue;
158                }
159                if (*bestmci == NULL)
160                        continue;
161                if (mci->mci_lastuse < (*bestmci)->mci_lastuse)
162                        bestmci = &MciCache[i];
163        }
164        return bestmci;
165}
166/*
167**  MCI_UNCACHE -- remove a connection from a slot.
168**
169**      May close a connection.
170**
171**      Parameters:
172**              mcislot -- the slot to empty.
173**              doquit -- if TRUE, send QUIT protocol on this connection.
174**                        if FALSE, we are assumed to be in a forked child;
175**                              all we want to do is close the file(s).
176**
177**      Returns:
178**              none.
179*/
180
181void
182mci_uncache(mcislot, doquit)
183        register MCI **mcislot;
184        bool doquit;
185{
186        register MCI *mci;
187        extern ENVELOPE BlankEnvelope;
188
189        mci = *mcislot;
190        if (mci == NULL)
191                return;
192        *mcislot = NULL;
193        if (mci->mci_host == NULL)
194                return;
195
196        mci_unlock_host(mci);
197
198        if (tTd(42, 5))
199                printf("mci_uncache: uncaching %lx (%s) from slot %d (%d)\n",
200                        (u_long) mci, mci->mci_host,
201                        (int)(mcislot - MciCache), doquit);
202        if (tTd(91, 100))
203                sm_syslog(LOG_DEBUG, CurEnv->e_id,
204                        "mci_uncache: uncaching %x (%.100s) from slot %d (%d)",
205                        mci, mci->mci_host, mcislot - MciCache, doquit);
206
207#if SMTP
208        if (doquit)
209        {
210                message("Closing connection to %s", mci->mci_host);
211
212                mci->mci_flags &= ~MCIF_CACHED;
213
214                /* only uses the envelope to flush the transcript file */
215                if (mci->mci_state != MCIS_CLOSED)
216                        smtpquit(mci->mci_mailer, mci, &BlankEnvelope);
217#ifdef XLA
218                xla_host_end(mci->mci_host);
219#endif
220        }
221        else
222#endif
223        {
224                if (mci->mci_in != NULL)
225                        xfclose(mci->mci_in, "mci_uncache", "mci_in");
226                if (mci->mci_out != NULL)
227                        xfclose(mci->mci_out, "mci_uncache", "mci_out");
228                mci->mci_in = mci->mci_out = NULL;
229                mci->mci_state = MCIS_CLOSED;
230                mci->mci_exitstat = EX_OK;
231                mci->mci_errno = 0;
232                mci->mci_flags = 0;
233        }
234}
235/*
236**  MCI_FLUSH -- flush the entire cache
237**
238**      Parameters:
239**              doquit -- if TRUE, send QUIT protocol.
240**                        if FALSE, just close the connection.
241**              allbut -- but leave this one open.
242**
243**      Returns:
244**              none.
245*/
246
247void
248mci_flush(doquit, allbut)
249        bool doquit;
250        MCI *allbut;
251{
252        register int i;
253
254        if (MciCache == NULL)
255                return;
256
257        for (i = 0; i < MaxMciCache; i++)
258                if (allbut != MciCache[i])
259                        mci_uncache(&MciCache[i], doquit);
260}
261/*
262**  MCI_GET -- get information about a particular host
263*/
264
265MCI *
266mci_get(host, m)
267        char *host;
268        MAILER *m;
269{
270        register MCI *mci;
271        register STAB *s;
272
273#if DAEMON
274        extern SOCKADDR CurHostAddr;
275
276        /* clear CurHostAddr so we don't get a bogus address with this name */
277        bzero(&CurHostAddr, sizeof CurHostAddr);
278#endif
279
280        /* clear out any expired connections */
281        (void) mci_scan(NULL);
282
283        if (m->m_mno < 0)
284                syserr("negative mno %d (%s)", m->m_mno, m->m_name);
285        s = stab(host, ST_MCI + m->m_mno, ST_ENTER);
286        mci = &s->s_mci;
287        mci->mci_host = s->s_name;
288
289        if (!mci_load_persistent(mci))
290        {
291                if (tTd(42, 2))
292                        printf("mci_get(%s %s): lock failed\n", host, m->m_name);
293                mci->mci_exitstat = EX_TEMPFAIL;
294                mci->mci_state = MCIS_CLOSED;
295                mci->mci_statfile = NULL;
296                return mci;
297        }
298
299        if (tTd(42, 2))
300        {
301                printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n",
302                        host, m->m_name, mci->mci_state, mci->mci_flags,
303                        mci->mci_exitstat, mci->mci_errno);
304        }
305
306#if SMTP
307        if (mci->mci_state == MCIS_OPEN)
308        {
309                extern int smtpprobe __P((MCI *));
310
311                /* poke the connection to see if it's still alive */
312                (void) smtpprobe(mci);
313
314                /* reset the stored state in the event of a timeout */
315                if (mci->mci_state != MCIS_OPEN)
316                {
317                        mci->mci_errno = 0;
318                        mci->mci_exitstat = EX_OK;
319                        mci->mci_state = MCIS_CLOSED;
320                }
321# if DAEMON
322                else
323                {
324                        /* get peer host address for logging reasons only */
325                        /* (this should really be in the mci struct) */
326                        SOCKADDR_LEN_T socklen = sizeof CurHostAddr;
327
328                        (void) getpeername(fileno(mci->mci_in),
329                                (struct sockaddr *) &CurHostAddr, &socklen);
330                }
331# endif
332        }
333#endif
334        if (mci->mci_state == MCIS_CLOSED)
335        {
336                time_t now = curtime();
337
338                /* if this info is stale, ignore it */
339                if (now > mci->mci_lastuse + MciInfoTimeout)
340                {
341                        mci->mci_lastuse = now;
342                        mci->mci_errno = 0;
343                        mci->mci_exitstat = EX_OK;
344                }
345        }
346
347        return mci;
348}
349/*
350**  MCI_SETSTAT -- set status codes in MCI structure.
351**
352**      Parameters:
353**              mci -- the MCI structure to set.
354**              xstat -- the exit status code.
355**              dstat -- the DSN status code.
356**              rstat -- the SMTP status code.
357**
358**      Returns:
359**              none.
360*/
361
362void
363mci_setstat(mci, xstat, dstat, rstat)
364        MCI *mci;
365        int xstat;
366        char *dstat;
367        char *rstat;
368{
369        /* protocol errors should never be interpreted as sticky */
370        if (xstat != EX_NOTSTICKY && xstat != EX_PROTOCOL)
371                mci->mci_exitstat = xstat;
372
373        mci->mci_status = dstat;
374        if (mci->mci_rstatus != NULL)
375                free(mci->mci_rstatus);
376        if (rstat != NULL)
377                rstat = newstr(rstat);
378        mci->mci_rstatus = rstat;
379}
380/*
381**  MCI_DUMP -- dump the contents of an MCI structure.
382**
383**      Parameters:
384**              mci -- the MCI structure to dump.
385**
386**      Returns:
387**              none.
388**
389**      Side Effects:
390**              none.
391*/
392
393struct mcifbits
394{
395        int     mcif_bit;       /* flag bit */
396        char    *mcif_name;     /* flag name */
397};
398struct mcifbits MciFlags[] =
399{
400        { MCIF_VALID,           "VALID"         },
401        { MCIF_TEMP,            "TEMP"          },
402        { MCIF_CACHED,          "CACHED"        },
403        { MCIF_ESMTP,           "ESMTP"         },
404        { MCIF_EXPN,            "EXPN"          },
405        { MCIF_SIZE,            "SIZE"          },
406        { MCIF_8BITMIME,        "8BITMIME"      },
407        { MCIF_7BIT,            "7BIT"          },
408        { MCIF_MULTSTAT,        "MULTSTAT"      },
409        { MCIF_INHEADER,        "INHEADER"      },
410        { MCIF_CVT8TO7,         "CVT8TO7"       },
411        { MCIF_DSN,             "DSN"           },
412        { MCIF_8BITOK,          "8BITOK"        },
413        { MCIF_CVT7TO8,         "CVT7TO8"       },
414        { MCIF_INMIME,          "INMIME"        },
415        { 0,                    NULL }
416};
417
418
419void
420mci_dump(mci, logit)
421        register MCI *mci;
422        bool logit;
423{
424        register char *p;
425        char *sep;
426        char buf[4000];
427        extern char *ctime();
428
429        sep = logit ? " " : "\n\t";
430        p = buf;
431        snprintf(p, SPACELEFT(buf, p), "MCI@%x: ", mci);
432        p += strlen(p);
433        if (mci == NULL)
434        {
435                snprintf(p, SPACELEFT(buf, p), "NULL");
436                goto printit;
437        }
438        snprintf(p, SPACELEFT(buf, p), "flags=%x", mci->mci_flags);
439        p += strlen(p);
440        if (mci->mci_flags != 0)
441        {
442                struct mcifbits *f;
443
444                *p++ = '<';
445                for (f = MciFlags; f->mcif_bit != 0; f++)
446                {
447                        if (!bitset(f->mcif_bit, mci->mci_flags))
448                                continue;
449                        snprintf(p, SPACELEFT(buf, p), "%s,", f->mcif_name);
450                        p += strlen(p);
451                }
452                p[-1] = '>';
453        }
454        snprintf(p, SPACELEFT(buf, p),
455                ",%serrno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s",
456                sep, mci->mci_errno, mci->mci_herrno,
457                mci->mci_exitstat, mci->mci_state, mci->mci_pid, sep);
458        p += strlen(p);
459        snprintf(p, SPACELEFT(buf, p),
460                "maxsize=%ld, phase=%s, mailer=%s,%s",
461                mci->mci_maxsize,
462                mci->mci_phase == NULL ? "NULL" : mci->mci_phase,
463                mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name,
464                sep);
465        p += strlen(p);
466        snprintf(p, SPACELEFT(buf, p),
467                "status=%s, rstatus=%s,%s",
468                mci->mci_status == NULL ? "NULL" : mci->mci_status,
469                mci->mci_rstatus == NULL ? "NULL" : mci->mci_rstatus,
470                sep);
471        p += strlen(p);
472        snprintf(p, SPACELEFT(buf, p),
473                "host=%s, lastuse=%s",
474                mci->mci_host == NULL ? "NULL" : mci->mci_host,
475                ctime(&mci->mci_lastuse));
476printit:
477        if (logit)
478                sm_syslog(LOG_DEBUG, CurEnv->e_id, "%.1000s", buf);
479        else
480                printf("%s\n", buf);
481}
482/*
483**  MCI_DUMP_ALL -- print the entire MCI cache
484**
485**      Parameters:
486**              logit -- if set, log the result instead of printing
487**                      to stdout.
488**
489**      Returns:
490**              none.
491*/
492
493void
494mci_dump_all(logit)
495        bool logit;
496{
497        register int i;
498
499        if (MciCache == NULL)
500                return;
501
502        for (i = 0; i < MaxMciCache; i++)
503                mci_dump(MciCache[i], logit);
504}
505/*
506**  MCI_LOCK_HOST -- Lock host while sending.
507**
508**      If we are contacting a host, we'll need to
509**      update the status information in the host status
510**      file, and if we want to do that, we ought to have
511**      locked it. This has the (according to some)
512**      desirable effect of serializing connectivity with
513**      remote hosts -- i.e.: one connection to a give
514**      host at a time.
515**
516**      Parameters:
517**              mci -- containing the host we want to lock.
518**
519**      Returns:
520**              EX_OK      -- got the lock.
521**              EX_TEMPFAIL -- didn't get the lock.
522*/
523
524int
525mci_lock_host(mci)
526        MCI *mci;
527{
528        if (mci == NULL)
529        {
530                if (tTd(56, 1))
531                        printf("mci_lock_host: NULL mci\n");
532                return EX_OK;
533        }
534
535        if (!SingleThreadDelivery)
536                return EX_OK;
537
538        return mci_lock_host_statfile(mci);
539}
540
541int
542mci_lock_host_statfile(mci)
543        MCI *mci;
544{
545        int savedErrno = errno;
546        int retVal = EX_OK;
547        char fname[MAXPATHLEN+1];
548
549        if (HostStatDir == NULL || mci->mci_host == NULL)
550                return EX_OK;
551
552        if (tTd(56, 2))
553                printf("mci_lock_host: attempting to lock %s\n",
554                        mci->mci_host);
555
556        if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname, TRUE) < 0)
557        {
558                /* of course this should never happen */
559                if (tTd(56, 2))
560                        printf("mci_lock_host: Failed to generate host path for %s\n",
561                               mci->mci_host);
562
563                retVal = EX_TEMPFAIL;
564                goto cleanup;
565        }
566
567        mci->mci_statfile = safefopen(fname, O_RDWR, FileMode,
568                   SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH|SFF_CREAT);
569
570        if (mci->mci_statfile == NULL)
571        {
572                syserr("mci_lock_host: cannot create host lock file %s",
573                               fname);
574                goto cleanup;
575        }
576
577        if (!lockfile(fileno(mci->mci_statfile), fname, "", LOCK_EX|LOCK_NB))
578        {
579                if (tTd(56, 2))
580                        printf("mci_lock_host: couldn't get lock on %s\n",
581                               fname);
582                fclose(mci->mci_statfile);
583                mci->mci_statfile = NULL;
584                retVal = EX_TEMPFAIL;
585                goto cleanup;
586        }
587
588        if (tTd(56, 12) && mci->mci_statfile != NULL)
589                printf("mci_lock_host: Sanity check -- lock is good\n");
590
591cleanup:
592        errno = savedErrno;
593        return retVal;
594}
595/*
596**  MCI_UNLOCK_HOST -- unlock host
597**
598**      Clean up the lock on a host, close the file, let
599**      someone else use it.
600**
601**      Parameters:
602**              mci -- us.
603**
604**      Returns:
605**              nothing.
606*/
607
608void
609mci_unlock_host(mci)
610        MCI *mci;
611{
612        int saveErrno = errno;
613
614        if (mci == NULL)
615        {
616                if (tTd(56, 1))
617                        printf("mci_unlock_host: NULL mci\n");
618                return;
619        }
620
621        if (HostStatDir == NULL || mci->mci_host == NULL)
622                return;
623
624        if (!SingleThreadDelivery && mci_lock_host_statfile(mci) == EX_TEMPFAIL)
625        {
626                if (tTd(56, 1))
627                        printf("mci_unlock_host: stat file already locked\n");
628        }
629        else
630        {
631                if (tTd(56, 2))
632                        printf("mci_unlock_host: store prior to unlock\n");
633
634                mci_store_persistent(mci);
635        }
636
637        if (mci->mci_statfile != NULL)
638        {
639                fclose(mci->mci_statfile);
640                mci->mci_statfile = NULL;
641        }
642
643        errno = saveErrno;
644}
645/*
646**  MCI_LOAD_PERSISTENT -- load persistent host info
647**
648**      Load information about host that is kept
649**      in common for all running sendmails.
650**
651**      Parameters:
652**              mci -- the host/connection to load persistent info
653**                         for.
654**
655**      Returns:
656**              TRUE -- lock was successful
657**              FALSE -- lock failed
658*/
659
660bool
661mci_load_persistent(mci)
662        MCI *mci;
663{
664        int saveErrno = errno;
665        bool locked = TRUE;
666        FILE *fp;
667        char fname[MAXPATHLEN+1];
668
669        if (mci == NULL)
670        {
671                if (tTd(56, 1))
672                        printf("mci_load_persistent: NULL mci\n");
673                return TRUE;
674        }
675
676        if (IgnoreHostStatus || HostStatDir == NULL || mci->mci_host == NULL)
677                return TRUE;
678       
679        /* Already have the persistent information in memory */
680        if (SingleThreadDelivery && mci->mci_statfile != NULL)
681                return TRUE;
682
683        if (tTd(56, 1))
684                printf("mci_load_persistent: Attempting to load persistent information for %s\n",
685                       mci->mci_host);
686               
687        if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname, FALSE) < 0)
688        {
689                /* Not much we can do if the file isn't there... */
690                if (tTd(56, 1))
691                        printf("mci_load_persistent: Couldn't generate host path\n");
692                goto cleanup;
693        }
694
695        fp = safefopen(fname, O_RDONLY, FileMode,
696                       SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH);
697        if (fp == NULL)
698        {
699                /* I can't think of any reason this should ever happen */
700                if (tTd(56, 1))
701                        printf("mci_load_persistent: open(%s): %s\n",
702                                fname, errstring(errno));
703                goto cleanup;
704        }
705
706        FileName = fname;
707        locked = lockfile(fileno(fp), fname, "", LOCK_SH|LOCK_NB);
708        (void) mci_read_persistent(fp, mci);
709        FileName = NULL;
710        if (locked)
711                lockfile(fileno(fp), fname, "", LOCK_UN);
712        fclose(fp);
713
714cleanup:
715        errno = saveErrno;
716        return locked;
717}
718/*
719**  MCI_READ_PERSISTENT -- read persistent host status file
720**
721**      Parameters:
722**              fp -- the file pointer to read.
723**              mci -- the pointer to fill in.
724**
725**      Returns:
726**              -1 -- if the file was corrupt.
727**              0 -- otherwise.
728**
729**      Warning:
730**              This code makes the assumption that this data
731**              will be read in an atomic fashion, and that the data
732**              was written in an atomic fashion.  Any other functioning
733**              may lead to some form of insanity.  This should be
734**              perfectly safe due to underlying stdio buffering.
735*/
736
737int
738mci_read_persistent(fp, mci)
739        FILE *fp;
740        register MCI *mci;
741{
742        int ver;
743        register char *p;
744        int saveLineNumber = LineNumber;
745        char buf[MAXLINE];
746
747        if (fp == NULL)
748                syserr("mci_read_persistent: NULL fp");
749        if (mci == NULL)
750                syserr("mci_read_persistent: NULL mci");
751        if (tTd(56, 93))
752        {
753                printf("mci_read_persistent: fp=%lx, mci=", (u_long) fp);
754                mci_dump(mci, FALSE);
755        }
756
757        mci->mci_status = NULL;
758        if (mci->mci_rstatus != NULL)
759                free(mci->mci_rstatus);
760        mci->mci_rstatus = NULL;
761
762        rewind(fp);
763        ver = -1;
764        LineNumber = 0;
765        while (fgets(buf, sizeof buf, fp) != NULL)
766        {
767                LineNumber++;
768                p = strchr(buf, '\n');
769                if (p != NULL)
770                        *p = '\0';
771                switch (buf[0])
772                {
773                  case 'V':             /* version stamp */
774                        ver = atoi(&buf[1]);
775                        if (ver < 0 || ver > 0)
776                                syserr("Unknown host status version %d: %d max",
777                                        ver, 0);
778                        break;
779
780                  case 'E':             /* UNIX error number */
781                        mci->mci_errno = atoi(&buf[1]);
782                        break;
783
784                  case 'H':             /* DNS error number */
785                        mci->mci_herrno = atoi(&buf[1]);
786                        break;
787
788                  case 'S':             /* UNIX exit status */
789                        mci->mci_exitstat = atoi(&buf[1]);
790                        break;
791
792                  case 'D':             /* DSN status */
793                        mci->mci_status = newstr(&buf[1]);
794                        break;
795
796                  case 'R':             /* SMTP status */
797                        mci->mci_rstatus = newstr(&buf[1]);
798                        break;
799
800                  case 'U':             /* last usage time */
801                        mci->mci_lastuse = atol(&buf[1]);
802                        break;
803
804                  case '.':             /* end of file */
805                        return 0;
806
807                  default:
808                        sm_syslog(LOG_CRIT, NOQID,
809                                  "%s: line %d: Unknown host status line \"%s\"",
810                                  FileName == NULL ? mci->mci_host : FileName,
811                                  LineNumber, buf);
812                        LineNumber = saveLineNumber;
813                        return -1;
814                }
815        }
816        LineNumber = saveLineNumber;
817        if (ver < 0)
818                return -1;
819        return 0;
820}
821/*
822**  MCI_STORE_PERSISTENT -- Store persistent MCI information
823**
824**      Store information about host that is kept
825**      in common for all running sendmails.
826**
827**      Parameters:
828**              mci -- the host/connection to store persistent info for.
829**
830**      Returns:
831**              none.
832*/
833
834void
835mci_store_persistent(mci)
836        MCI *mci;
837{
838        int saveErrno = errno;
839
840        if (mci == NULL)
841        {
842                if (tTd(56, 1))
843                        printf("mci_store_persistent: NULL mci\n");
844                return;
845        }
846
847        if (HostStatDir == NULL || mci->mci_host == NULL)
848                return;
849
850        if (tTd(56, 1))
851                printf("mci_store_persistent: Storing information for %s\n",
852                           mci->mci_host);
853
854        if (mci->mci_statfile == NULL)
855        {
856                if (tTd(56, 1))
857                        printf("mci_store_persistent: no statfile\n");
858                return;
859        }
860
861        rewind(mci->mci_statfile);
862#if !NOFTRUNCATE
863        (void) ftruncate(fileno(mci->mci_statfile), (off_t) 0);
864#endif
865
866        fprintf(mci->mci_statfile, "V0\n");
867        fprintf(mci->mci_statfile, "E%d\n", mci->mci_errno);
868        fprintf(mci->mci_statfile, "H%d\n", mci->mci_herrno);
869        fprintf(mci->mci_statfile, "S%d\n", mci->mci_exitstat);
870        if (mci->mci_status != NULL)
871                fprintf(mci->mci_statfile, "D%.80s\n",
872                        denlstring(mci->mci_status, TRUE, FALSE));
873        if (mci->mci_rstatus != NULL)
874                fprintf(mci->mci_statfile, "R%.80s\n",
875                        denlstring(mci->mci_rstatus, TRUE, FALSE));
876        fprintf(mci->mci_statfile, "U%ld\n", (long)(mci->mci_lastuse));
877        fprintf(mci->mci_statfile, ".\n");
878
879        fflush(mci->mci_statfile);
880
881        errno = saveErrno;
882        return;
883}
884/*
885**  MCI_TRAVERSE_PERSISTENT -- walk persistent status tree
886**
887**      Recursively find all the mci host files in `pathname'.  Default to
888**              main host status directory if no path is provided.
889**      Call (*action)(pathname, host) for each file found.
890**
891**      Note: all information is collected in a list before it is processed.
892**      This may not be the best way to do it, but it seems safest, since
893**      the file system would be touched while we are attempting to traverse
894**      the directory tree otherwise (during purges).
895**
896**      Parameters:
897**              action -- function to call on each node.  If returns < 0,
898**                      return immediately.
899**              pathname -- root of tree.  If null, use main host status
900**                      directory.
901**
902**      Returns:
903**              < 0 -- if any action routine returns a negative value, that
904**                      value is returned.
905**              0 -- if we successfully went to completion.
906*/
907
908int
909mci_traverse_persistent(action, pathname)
910        int (*action)();
911        char *pathname;
912{
913        struct stat statbuf;
914        DIR *d;
915        int ret;
916
917        if (pathname == NULL)
918                pathname = HostStatDir;
919        if (pathname == NULL)
920                return -1;
921
922        if (tTd(56, 1))
923                printf("mci_traverse: pathname is %s\n", pathname);
924
925        ret = stat(pathname, &statbuf);
926        if (ret < 0)
927        {
928                if (tTd(56, 2))
929                        printf("mci_traverse: Failed to stat %s: %s\n",
930                                pathname, errstring(errno));
931                return ret;
932        }
933        if (S_ISDIR(statbuf.st_mode))
934        {
935                struct dirent *e;
936                char *newptr;
937                char newpath[MAXPATHLEN+1];
938
939                if ((d = opendir(pathname)) == NULL)
940                {
941                        if (tTd(56, 2))
942                                printf("mci_traverse: opendir %s: %s\n",
943                                        pathname, errstring(errno));
944                        return -1;
945                }
946
947                if (strlen(pathname) >= sizeof newpath - MAXNAMLEN - 3)
948                {
949                        if (tTd(56, 2))
950                                printf("mci_traverse: path \"%s\" too long",
951                                        pathname);
952                        return -1;
953                }
954                strcpy(newpath, pathname);
955                newptr = newpath + strlen(newpath);
956                *newptr++ = '/';
957
958                while ((e = readdir(d)) != NULL)
959                {
960                        if (e->d_name[0] == '.')
961                                continue;
962
963                        strncpy(newptr, e->d_name,
964                                sizeof newpath - (newptr - newpath) - 1);
965                        newpath[sizeof newpath - 1] = '\0';
966
967                        ret = mci_traverse_persistent(action, newpath);
968                        if (ret < 0)
969                                break;
970
971                        /*
972                        **  The following appears to be
973                        **  necessary during purges, since
974                        **  we modify the directory structure
975                        */
976
977                        if (action == mci_purge_persistent)
978                                rewinddir(d);
979                }
980
981                /* purge (or whatever) the directory proper */
982                *--newptr = '\0';
983                ret = (*action)(newpath, NULL);
984                closedir(d);
985        }
986        else if (S_ISREG(statbuf.st_mode))
987        {
988                char *end = pathname + strlen(pathname) - 1;
989                char *start;
990                char *scan;
991                char host[MAXHOSTNAMELEN];
992                char *hostptr = host;
993               
994                /*
995                **  Reconstruct the host name from the path to the
996                **  persistent information.
997                */
998
999                do
1000                {
1001                        if (hostptr != host)
1002                                *(hostptr++) = '.';
1003                        start = end;
1004                        while (*(start - 1) != '/')
1005                                start--;
1006
1007                        if (*end == '.')
1008                                end--;
1009
1010                        for (scan = start; scan <= end; scan++)
1011                                *(hostptr++) = *scan;
1012
1013                        end = start - 2;
1014                } while (*end == '.');
1015
1016                *hostptr = '\0';
1017
1018                /*
1019                **  Do something with the file containing the persistent
1020                **  information.
1021                */
1022                ret = (*action)(pathname, host);
1023        }
1024
1025        return ret;
1026}
1027/*
1028**  MCI_PRINT_PERSISTENT -- print persisten info
1029**
1030**      Dump the persistent information in the file 'pathname'
1031**
1032**      Parameters:
1033**              pathname -- the pathname to the status file.
1034**              hostname -- the corresponding host name.
1035**
1036**      Returns:
1037**              0
1038*/
1039
1040int
1041mci_print_persistent(pathname, hostname)
1042        char *pathname;
1043        char *hostname;
1044{
1045        static int initflag = FALSE;
1046        FILE *fp;
1047        int width = Verbose ? 78 : 25;
1048        bool locked;
1049        MCI mcib;
1050
1051        /* skip directories */
1052        if (hostname == NULL)
1053                return 0;
1054
1055        if (!initflag)
1056        {
1057                initflag = TRUE;
1058                printf(" -------------- Hostname --------------- How long ago ---------Results---------\n");
1059        }
1060
1061        fp = safefopen(pathname, O_RDWR, FileMode,
1062                       SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH);
1063
1064        if (fp == NULL)
1065        {
1066                if (tTd(56, 1))
1067                        printf("mci_print_persistent: cannot open %s: %s\n",
1068                                pathname, errstring(errno));
1069                return 0;
1070        }
1071
1072        FileName = pathname;
1073        bzero(&mcib, sizeof mcib);
1074        if (mci_read_persistent(fp, &mcib) < 0)
1075        {
1076                syserr("%s: could not read status file", pathname);
1077                fclose(fp);
1078                FileName = NULL;
1079                return 0;
1080        }
1081
1082        locked = !lockfile(fileno(fp), pathname, "", LOCK_EX|LOCK_NB);
1083        fclose(fp);
1084        FileName = NULL;
1085
1086        printf("%c%-39s %12s ",
1087                locked ? '*' : ' ', hostname,
1088                pintvl(curtime() - mcib.mci_lastuse, TRUE));
1089        if (mcib.mci_rstatus != NULL)
1090                printf("%.*s\n", width, mcib.mci_rstatus);
1091        else if (mcib.mci_exitstat == EX_TEMPFAIL && mcib.mci_errno != 0)
1092                printf("Deferred: %.*s\n", width - 10, errstring(mcib.mci_errno));
1093        else if (mcib.mci_exitstat != 0)
1094        {
1095                int i = mcib.mci_exitstat - EX__BASE;
1096                extern int N_SysEx;
1097                extern char *SysExMsg[];
1098
1099                if (i < 0 || i >= N_SysEx)
1100                {
1101                        char buf[80];
1102
1103                        snprintf(buf, sizeof buf, "Unknown mailer error %d",
1104                                mcib.mci_exitstat);
1105                        printf("%.*s\n", width, buf);
1106                }
1107                else
1108                        printf("%.*s\n", width, &(SysExMsg[i])[5]);
1109        }
1110        else if (mcib.mci_errno == 0)
1111                printf("OK\n");
1112        else
1113                printf("OK: %.*s\n", width - 4, errstring(mcib.mci_errno));
1114
1115        return 0;
1116}
1117/*
1118**  MCI_PURGE_PERSISTENT -- Remove a persistence status file.
1119**
1120**      Parameters:
1121**              pathname -- path to the status file.
1122**              hostname -- name of host corresponding to that file.
1123**                      NULL if this is a directory (domain).
1124**
1125**      Returns:
1126**              0
1127*/
1128
1129int
1130mci_purge_persistent(pathname, hostname)
1131        char *pathname;
1132        char *hostname;
1133{
1134        char *end = pathname + strlen(pathname) - 1;
1135
1136        if (tTd(56, 1))
1137                printf("mci_purge_persistent: purging %s\n", pathname);
1138
1139        if (hostname != NULL)
1140        {
1141                /* remove the file */
1142                if (unlink(pathname) < 0)
1143                {
1144                        if (tTd(56, 2))
1145                                printf("mci_purge_persistent: failed to unlink %s: %s\n",
1146                                        pathname, errstring(errno));
1147                }
1148        }
1149        else
1150        {
1151                /* remove the directory */
1152                if (*end != '.')
1153                        return 0;
1154
1155                if (tTd(56, 1))
1156                        printf("mci_purge_persistent: dpurge %s\n", pathname);
1157
1158                if (rmdir(pathname) < 0)
1159                {
1160                        if (tTd(56, 2))
1161                                printf("mci_purge_persistent: rmdir %s: %s\n",
1162                                        pathname, errstring(errno));
1163                }
1164               
1165        }
1166
1167        return 0;
1168}
1169/*
1170**  MCI_GENERATE_PERSISTENT_PATH -- generate path from hostname
1171**
1172**      Given `host', convert from a.b.c to $QueueDir/.hoststat/c./b./a,
1173**      putting the result into `path'.  if `createflag' is set, intervening
1174**      directories will be created as needed.
1175**
1176**      Parameters:
1177**              host -- host name to convert from.
1178**              path -- place to store result.
1179**              pathlen -- length of path buffer.
1180**              createflag -- if set, create intervening directories as
1181**                      needed.
1182**
1183**      Returns:
1184**              0 -- success
1185**              -1 -- failure
1186*/
1187
1188int
1189mci_generate_persistent_path(host, path, pathlen, createflag)
1190        const char *host;
1191        char *path;
1192        int pathlen;
1193        bool createflag;
1194{
1195        char *elem, *p, *x, ch;
1196        int ret = 0;
1197        int len;
1198        char t_host[MAXHOSTNAMELEN];
1199
1200        /*
1201        **  Rationality check the arguments.
1202        */
1203
1204        if (host == NULL)
1205        {
1206                syserr("mci_generate_persistent_path: null host");
1207                return -1;
1208        }
1209        if (path == NULL)
1210        {
1211                syserr("mci_generate_persistent_path: null path");
1212                return -1;
1213        }
1214
1215        if (tTd(56, 80))
1216                printf("mci_generate_persistent_path(%s): ", host);
1217
1218        if (*host == '\0' || *host == '.')
1219                return -1;
1220
1221        /* make certain this is not a bracketed host number */
1222        if (strlen(host) > sizeof t_host - 1)
1223                return -1;
1224        if (host[0] == '[')
1225                strcpy(t_host, host + 1);
1226        else   
1227                strcpy(t_host, host);
1228
1229        /*
1230        **  Delete any trailing dots from the hostname.
1231        **  Leave 'elem' pointing at the \0.
1232        */
1233
1234        elem = t_host + strlen(t_host);
1235        while (elem > t_host &&
1236               (elem[-1] == '.' || (host[0] == '[' && elem[-1] == ']')))
1237                *--elem = '\0';
1238
1239        /* check for bogus bracketed address */
1240        if (host[0] == '[' && inet_addr(t_host) == INADDR_NONE)
1241                return -1;
1242
1243        /* check for what will be the final length of the path */
1244        len = strlen(HostStatDir) + 2;
1245        for (p = (char *) t_host; *p != '\0'; p++)
1246        {
1247                if (*p == '.')
1248                        len++;
1249                len++;
1250                if (p[0] == '.' && p[1] == '.')
1251                        return -1;
1252        }
1253        if (len > pathlen || len < 1)
1254                return -1;
1255
1256        strcpy(path, HostStatDir);
1257        p = path + strlen(path);
1258
1259        while (elem > t_host)
1260        {
1261                if (!path_is_dir(path, createflag))
1262                {
1263                        ret = -1;
1264                        break;
1265                }
1266                elem--;
1267                while (elem >= t_host && *elem != '.')
1268                        elem--;
1269                *p++ = '/';
1270                x = elem + 1;
1271                while ((ch = *x++) != '\0' && ch != '.')
1272                {
1273                        if (isascii(ch) && isupper(ch))
1274                                ch = tolower(ch);
1275                        if (ch == '/')
1276                                ch = ':';       /* / -> : */
1277                        *p++ = ch;
1278                }
1279                if (elem >= t_host)
1280                        *p++ = '.';
1281                *p = '\0';
1282        }
1283
1284        if (tTd(56, 80))
1285        {
1286                if (ret < 0)
1287                        printf("FAILURE %d\n", ret);
1288                else
1289                        printf("SUCCESS %s\n", path);
1290        }
1291
1292        return (ret);
1293}
Note: See TracBrowser for help on using the repository browser.