Back to index

courier  0.68.2
Classes | Defines | Functions | Variables
courieresmtp.c File Reference
#include "courier.h"
#include "moduledel.h"
#include "maxlongsize.h"
#include "comreadtime.h"
#include <sys/types.h>
#include <sys/uio.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include "waitlib/waitlib.h"
#include "numlib/numlib.h"
#include "mybuf.h"
#include "esmtpconfig.h"
#include "rfc1035/rfc1035.h"

Go to the source code of this file.

Classes

struct  esmtpchildinfo

Defines

#define ESMTP_NOCHILD(i)   ( (i)->pid < 0)
#define ESMTP_BUSY(i)   (!ESMTP_NOCHILD(i) && (i)->cmdpipe >= 0 && (i)->isbusy)
#define ESMTP_IDLE(i)   (!ESMTP_NOCHILD(i) && (i)->cmdpipe >= 0 &&!(i)->isbusy)
#define ESMTP_TERMINATING(i)   (!ESMTP_NOCHILD(i) && (i)->cmdpipe < 0)

Functions

static void start_child (unsigned, char *, char **)
static void send_child (unsigned, char *, char **)
static void terminated_child (unsigned, unsigned)
static void esmtpparent ()
void esmtpchild (unsigned)
static int call_mybuf_get (void *p)
int isloopback (const char *ip)
int main (int argc, char **argv)
static int parse_ack (const char *msg, unsigned *index, pid_t *pid)

Variables

static time_t esmtpkeepalive
static struct esmtpchildinfoinfo
static int completionpipe [2]
static FILE * childresultpipe
static struct mybuf
static struct rfc1035_ifconfinterfaces = NULL

Class Documentation

struct esmtpchildinfo

Definition at line 41 of file courieresmtp.c.

Class Members
int cmdpipe
char * host
int isbusy
char * pendel
pid_t pid
time_t termtime

Define Documentation

#define ESMTP_BUSY (   i)    (!ESMTP_NOCHILD(i) && (i)->cmdpipe >= 0 && (i)->isbusy)

Definition at line 58 of file courieresmtp.c.

#define ESMTP_IDLE (   i)    (!ESMTP_NOCHILD(i) && (i)->cmdpipe >= 0 &&!(i)->isbusy)

Definition at line 61 of file courieresmtp.c.

#define ESMTP_NOCHILD (   i)    ( (i)->pid < 0)

Definition at line 55 of file courieresmtp.c.

#define ESMTP_TERMINATING (   i)    (!ESMTP_NOCHILD(i) && (i)->cmdpipe < 0)

Definition at line 64 of file courieresmtp.c.


Function Documentation

static int call_mybuf_get ( void *  p) [static]

Definition at line 81 of file courieresmtp.c.

{
       return (mybuf_get( (struct mybuf *)p ));
}

Here is the caller graph for this function:

void esmtpchild ( unsigned  )

Definition at line 147 of file esmtpclient.c.

{
struct moduledel *del;
struct ctlfile       ctf;
unsigned long mypid=(unsigned long)getpid();

       signal(SIGPIPE, SIG_IGN);
       srand(time(NULL));
       rw_init_courier("esmtp");
       rewrite_func=rw_search_transport("esmtp")->rw_ptr->rewrite;

       if (chdir(courierdir()))
              clog_msg_errno();
       sockfd= -1;

       setconfig();

       net_timeout=0;

#ifdef TCP_CORK

       {
       const char *p=getenv("ESMTP_CORK");

              esmtp_cork=p ? atoi(p):0;
              corked=0;
       }

#endif

       /* Retrieve delivery request until courieresmtp closes the pipe */

       while ((del=module_getdel()) != 0)
       {
       fd_set fdr;
       struct timeval       tv;
       const char *p;

#if 0
              clog_msg_start_info();
              clog_msg_str("Process ");
              clog_msg_uint(getpid());
              clog_msg_str(" ready to be grabbed.");
              clog_msg_send();
              sleep(60);
#endif

              /*
              ** Open the message control file, send the message, close
              ** the control file, we're done.
              */

              if (ctlfile_openi(del->inum, &ctf, 0) == 0)
              {
                     int changed_vhosts=ctlfile_setvhost(&ctf);

                     if (changed_vhosts)
                     {
                            quit();
                            setconfig();
                     }

                     sendesmtp(del, &ctf);
                     ctlfile_close(&ctf);
              }
              {
              char   pidbuf[NUMBUFSIZE];

                     printf("%u %s\n", childnum, libmail_str_pid_t(mypid, pidbuf));
                     fflush(stdout);
              }

              /*
              ** While waiting for the next message, push a RSET every
              ** so-so seconds
              */

              while (esmtpkeepaliveping && sockfd >= 0)
              {
                     FD_ZERO(&fdr);
                     FD_SET(0, &fdr);
                     tv.tv_sec=esmtpkeepaliveping;
                     tv.tv_usec=0;

                     if ( sox_select(1, &fdr, 0, 0, &tv) > 0)
                            break;
                     sock_timeout(data_timeout);
                     if (dowritestr("RSET\r\n") || writeflush())
                            break;

                     while ( (p=readline()) != 0 && !ISFINALLINE(p))
                            ;

                     if (p == 0)
                     {
                            quit();
                            break;
                     }
              }
       }
       if (sockfd >= 0)
              quit();
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void esmtpparent ( ) [static]

Definition at line 148 of file courieresmtp.c.

{
unsigned i;
fd_set fdc, fds;
time_t current_time;

       libmail_changeuidgid(MAILUID, MAILGID);
       module_init(&terminated_child);

       if ((info=(struct esmtpchildinfo *)malloc(sizeof(*info)*
              module_nchildren)) == 0)
              clog_msg_errno();
       for (i=0; i<module_nchildren; i++)
       {
              info[i].pid= -1;
              info[i].cmdpipe= -1;
              info[i].host=0;
              info[i].pendel=0;
       }
       if (pipe(completionpipe) < 0)
              clog_msg_errno();

       if ((childresultpipe=fdopen(completionpipe[0], "r")) == 0)
              clog_msg_errno();
       FD_ZERO(&fdc);
       FD_SET(0, &fdc);
       FD_SET(completionpipe[0], &fdc);
       mybuf_init(&courierdbuf, 0);
       mybuf_init(&childbuf, completionpipe[0]);

       module_blockset();
       time(&current_time);

       for (;;)
       {
       time_t wait_time;
       struct timeval       tv;

              wait_time=0;
              for (i=0; i<module_nchildren; i++)
              {
                     if (!ESMTP_IDLE(&info[i]))  continue;
                     if (info[i].termtime <= current_time)
                     {
                            close(info[i].cmdpipe);
                            info[i].cmdpipe= -1;
                            continue;
                     }

                     if (wait_time == 0 || info[i].termtime < wait_time)
                            wait_time=info[i].termtime;
              }

              if (wait_time)
              {
                     tv.tv_sec= wait_time - current_time;
                     tv.tv_usec=0;
              }

              fds=fdc;

              module_blockclr();
                while (select(completionpipe[0]+1, &fds, (fd_set *)0, (fd_set *)0,
                                (wait_time ? &tv:(struct timeval *)0)) < 0)
                {
                        if (errno != EINTR)     clog_msg_errno();
                }

              module_blockset();
              time(&current_time);

              if (FD_ISSET(completionpipe[0], &fds))
              {
              char   *line;

                     do
                     {
                     pid_t  p;

                            line=module_getline( &call_mybuf_get,
                                          &childbuf);

                            if (parse_ack(line, &i, &p) ||
                                   i >= module_nchildren ||
                                   (p == info[i].pid &&
                                          !ESMTP_BUSY(&info[i])))
                            {
                                   clog_msg_start_err();
                                   clog_msg_str("INVALID message from child process.");
                                   clog_msg_send();
                                   _exit(0);
                            }
                            if (p != info[i].pid)       continue;
                            info[i].isbusy=0;
                            info[i].termtime=current_time + esmtpkeepalive;
                            if (info[i].pendel)
                            {
                                   free(info[i].pendel);
                                   info[i].pendel=0;
                            }

                            module_completed(i, module_delids[i]);
                     } while (mybuf_more(&childbuf));
              }

              if (!FD_ISSET(0, &fds))     continue;

              do
              {
              char   **cols;
              const char *hostp;
              size_t hostplen;
              time_t misctime;
              unsigned j;
              char   *line;

                     line=module_getline( &call_mybuf_get, &courierdbuf);
                     if (!line)
                     {
                            module_restore();

                            /*
                            ** If all processes are idle, wait for them
                            ** to finish normally.  Otherwise, kill
                            ** the processes.
                            */

                            for (j=0; j<module_nchildren; j++)
                                   if (ESMTP_BUSY(&info[j]))
                                          break;

                            if (j < module_nchildren)
                            {
                                   for (j=0; j<module_nchildren; j++)
                                          if (info[j].pid > 0)
                                                 kill(info[j].pid,
                                                        SIGTERM);
                            }
                            else
                            {
                            int    waitstat;

                                   for (j=0; j<module_nchildren; j++)
                                   {
                                          if (info[j].cmdpipe > 0)
                                          {
                                                 close(info[j].cmdpipe);
                                                 info[j].cmdpipe= -1;
                                          }
                                   }
                                   while (wait(&waitstat) != -1 ||
                                          errno == EINTR)
                                          ;
                            }
                            _exit(0);
                     }

                     cols=module_parsecols(line);

                     if (!cols)    _exit(0);

                     hostp=MODULEDEL_HOST(cols);
                     for (hostplen=0; hostp[hostplen] &&
                            hostp[hostplen] != '\t'; hostplen++)
                            ;

                     for (i=0; i<module_nchildren; i++)
                     {
                            if (!ESMTP_IDLE(&info[i])) continue;
                            if (memcmp(info[i].host, hostp, hostplen) == 0
                                   && info[i].host[hostplen] == 0)
                                   break;
                     }

                     if (i < module_nchildren)   /* Reuse a process */
                     {
                            send_child(i, line, cols);
                            continue;
                     }

                     for (i=0; i<module_nchildren; i++)
                            if (ESMTP_NOCHILD(&info[i]))       break;

                     if (i < module_nchildren)   /* We can fork */
                     {
                            start_child(i, line, cols);
                            send_child(i, line, cols);
                            continue;
                     }

                     /*
                     ** Find a process that's been idled the longest,
                     ** and reuse that one.
                     */

                     misctime=0;
                     j=0;
                     for (i=0; i<module_nchildren; i++)
                     {
                            if (ESMTP_IDLE(&info[i]) &&
                                   (misctime == 0 || misctime >
                                          info[i].termtime))
                            {
                                   j=i;
                                   misctime=info[i].termtime;
                            }
                     }
                     if (misctime)
                     {
                            if (info[j].pendel)
                            {
                                   clog_msg_start_err();
                                   clog_msg_str("INTERNAL ERROR: unexpected scheduled delivery.");
                                   clog_msg_send();
                                   _exit(1);
                            }

                            info[j].pendel=strcpy(
                                   courier_malloc(strlen(line)+1),
                                   line);
                            close(info[j].cmdpipe);
                            info[j].cmdpipe= -1;
                            continue;
                     }

                     /* The ONLY remaining possibility is something in
                     ** the TERMINATING stage, without another delivery
                     ** already scheduled for that slot.
                     */

                     for (i=0; i<module_nchildren; i++)
                     {
                            if (ESMTP_TERMINATING(&info[i]) &&
                                   info[i].pendel == 0)
                                   break;
                     }

                     if (i < module_nchildren)
                     {
                            info[i].pendel=strcpy(
                                   courier_malloc(strlen(line)+1),
                                   line);
                            continue;
                     }

                     clog_msg_start_err();
                     clog_msg_str("INTERNAL ERROR: unexpected delivery.");
                     clog_msg_send();
                     _exit(1);
              } while (mybuf_more(&courierdbuf));
       }
}

Here is the call graph for this function:

Here is the caller graph for this function:

int isloopback ( const char *  ip)

Definition at line 88 of file courieresmtp.c.

{
       struct rfc1035_ifconf *p;

       for (p=interfaces; p; p=p->next)
       {
              char buf[RFC1035_NTOABUFSIZE];

              rfc1035_ntoa(&p->ifaddr, buf);
              if (strcmp(buf, ip) == 0)
                     return (1);
       }
       return (0);
}

Here is the call graph for this function:

Here is the caller graph for this function:

int main ( int  argc,
char **  argv 
)

Definition at line 103 of file courieresmtp.c.

{
        clog_open_syslog("courieresmtp");
       esmtpkeepalive=config_time_esmtpkeepalive();

       if (argc < 2)
       {
              esmtpparent();
       }
       else
       {
              interfaces=rfc1035_ifconf(NULL);
              esmtpchild(atoi(argv[1]));
       }
       return (0);
}

Here is the call graph for this function:

static int parse_ack ( const char *  msg,
unsigned *  index,
pid_t *  pid 
) [static]

Definition at line 129 of file courieresmtp.c.

{
       *index=0;
       *pid=0;

       while (*msg != ' ')
       {
              if (*msg < '0' || *msg > '9')      return (-1);
              *index = *index * 10 + (*msg++ - '0');
       }
       ++msg;
       while (*msg)
       {
              if (*msg < '0' || *msg > '9')      return (-1);
              *pid = *pid * 10 + (*msg++ - '0');
       }
       return (0);
}

Here is the caller graph for this function:

static void send_child ( unsigned  i,
char *  line,
char **  cols 
) [static]

Definition at line 512 of file courieresmtp.c.

{
const char *p;
struct iovec  iov[2];
size_t l=strlen(line);

       module_delids[i]=0;

       for (p=MODULEDEL_DELID(cols); *p >= '0' && *p <= '9'; p++)
              module_delids[i] = module_delids[i] * 10 + (*p - '0');

       if (info[i].pid < 0) /* Failure in start_child */
       {
              terminated_child(i, module_delids[i]);
              return;
       }

        iov[0].iov_base=(caddr_t)line;
        iov[0].iov_len=l;
       iov[1].iov_base="\n";
       iov[1].iov_len=1;

       if (writev(info[i].cmdpipe, iov, 2) != l+1)
       {
              clog_msg_prerrno();
              /* This is usually because the process has terminated,
              ** but we haven't cleaned this up yet, but just make sure.
              */
              kill(info[i].pid, SIGKILL);
       }
       info[i].isbusy=1;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void start_child ( unsigned  i,
char *  line,
char **  cols 
) [static]

Definition at line 451 of file courieresmtp.c.

{
int    pipebuf[2];
const char *hostp;
size_t hostplen;
pid_t  pid;

       hostp=MODULEDEL_HOST(cols);
       for (hostplen=0; hostp[hostplen] &&
              hostp[hostplen] != '\t'; hostplen++)
              ;

       if (info[i].host)    free(info[i].host);
       memcpy(info[i].host=courier_malloc(hostplen+1), hostp, hostplen);
       info[i].host[hostplen]=0;

       if (pipe(pipebuf) < 0)      clog_msg_errno();

       pid=module_fork_noblock(0, &i);

       if (pid == 0)
       {
       unsigned      j;
       char   buf[MAXLONGSIZE+1];
       char *p;

              dup2(pipebuf[0], 0);
              close(pipebuf[0]);
              close(pipebuf[1]);
              fclose(childresultpipe);
              dup2(completionpipe[1], 1);
              close(completionpipe[1]);
              close(completionpipe[0]);
              for (j=0; j<module_nchildren; j++)
                     if (info[j].cmdpipe >= 0)
                            close(info[j].cmdpipe);

              for (j=0; MODULEDEL_HOST(cols)[j]; j++)
                     if (MODULEDEL_HOST(cols)[j] == '\t')
                     {
                            MODULEDEL_HOST(cols)[j]=0;
                            break;
                     }

              p=buf+MAXLONGSIZE;
              *p=0;
              do
              {
                     * --p = '0' + (i % 10);
              } while ( (i=i / 10) != 0);

              execl("courieresmtp", "courieresmtp", p,
                     MODULEDEL_HOST(cols), (char *)0);
              clog_msg_errno();
              _exit(1);
       }
       info[i].pid=pid;
       close (pipebuf[0]);
       info[i].cmdpipe=pipebuf[1];
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void terminated_child ( unsigned  idx,
unsigned  delid 
) [static]

Definition at line 401 of file courieresmtp.c.

{
       if (ESMTP_TERMINATING(&info[idx]))
       {
       char   *p;

              if ((p=info[idx].pendel) != 0)
              {
              char **cols=module_parsecols(info[idx].pendel);

                     /*
                     ** Clear to 0 to prevent infinite loop if fork fails
                     ** in start_child.
                     */
                     info[idx].pendel=0;
                     start_child(idx, info[idx].pendel, cols);
                     info[idx].pendel=p;
                     send_child(idx, info[idx].pendel, cols);
                     return;
              }

              info[idx].pid= -1;
              return;
       }

       /* Oops, something crashed.  Clean it up */

       clog_msg_start_err();
       if (info[idx].pid < 0)
       {
              clog_msg_str("Unable to fork");
       }
       else
       {
              clog_msg_str("Crashed child process ");
              clog_msg_uint(info[idx].pid);
       }

       if (ESMTP_BUSY(&info[idx]))
       {
              clog_msg_str(", while delivering to ");
              clog_msg_str(info[idx].host);
              module_completed(idx, delid);
       }
       clog_msg_send();
       close(info[idx].cmdpipe);
       info[idx].cmdpipe= -1;
       info[idx].pid= -1;
}

Here is the call graph for this function:

Here is the caller graph for this function:


Variable Documentation

FILE* childresultpipe [static]

Definition at line 75 of file courieresmtp.c.

int completionpipe[2] [static]

Definition at line 74 of file courieresmtp.c.

time_t esmtpkeepalive [static]

Definition at line 39 of file courieresmtp.c.

struct esmtpchildinfo* info [static]

Definition at line 68 of file courieresmtp.c.

struct rfc1035_ifconf* interfaces = NULL [static]

Definition at line 86 of file courieresmtp.c.

struct mybuf [static]

Definition at line 77 of file courieresmtp.c.