Don't pass EX_TEMPFAIL code out (since we have already taken responsibility
[unix-history] / usr / src / usr.sbin / sendmail / src / main.c
index 0736f0f..371d1f2 100644 (file)
@@ -1,12 +1,10 @@
 # define  _DEFINE
 # include <signal.h>
 # define  _DEFINE
 # include <signal.h>
-# include <pwd.h>
-# include <time.h>
 # include <sys/ioctl.h>
 # include "sendmail.h"
 # include <sys/ioctl.h>
 # include "sendmail.h"
-# include <sys/stat.h>
+# include <sys/file.h>
 
 
-SCCSID(@(#)main.c      3.95            %G%);
+SCCSID(@(#)main.c      3.157           %G%);
 
 /*
 **  SENDMAIL -- Post mail to a set of destinations.
 
 /*
 **  SENDMAIL -- Post mail to a set of destinations.
@@ -25,82 +23,18 @@ SCCSID(@(#)main.c   3.95            %G%);
 **     server mechanism).
 **
 **     Usage:
 **     server mechanism).
 **
 **     Usage:
-**             /etc/sendmail [flags] addr ...
+**             /usr/lib/sendmail [flags] addr ...
 **
 **
-**     Positional Parameters:
-**             addr -- the address to deliver the mail to.  There
-**                     can be several.
-**
-**     Flags:
-**             -f name         The mail is from "name" -- used for
-**                             the header in local mail, and to
-**                             deliver reports of failures to.
-**             -r name         Same as -f; however, this flag is
-**                             reserved to indicate special processing
-**                             for remote mail delivery as needed
-**                             in the future.  So, network servers
-**                             should use -r.
-**             -Ffullname      Select what the full-name should be
-**                             listed as.
-**             -a              This mail should be in ARPANET std
-**                             format (obsolete version).
-**             -as             Speak SMTP.
-**             -n              Don't do aliasing.  This might be used
-**                             when delivering responses, for
-**                             instance.
-**             -dN             Run with debugging set to level N.
-**             -em             Mail back a response if there was an
-**                             error in processing.  This should be
-**                             used when the origin of this message
-**                             is another machine.
-**             -ew             Write back a response if the user is
-**                             still logged in, otherwise, act like
-**                             -em.
-**             -eq             Don't print any error message (just
-**                             return exit status).
-**             -ep             (default)  Print error messages
-**                             normally.
-**             -ee             Send BerkNet style errors.  This
-**                             is equivalent to MailBack except
-**                             that it has gives zero return code
-**                             (unless there were errors during
-**                             returning).  This used to be
-**                             "EchoBack", but you know how the old
-**                             software bounces.
-**             -m              In group expansion, send to the
-**                             sender also (stands for the Mail metoo
-**                             option.
-**             -i              Do not terminate mail on a line
-**                             containing just dot.
-**             -s              Save UNIX-like "From" lines on the
-**                             front of messages.
-**             -v              Give blow-by-blow description of
-**                             everything that happens.
-**             -t              Read "to" addresses from message.
-**                             Looks at To:, Cc:, and Bcc: lines.
-**             -I              Initialize the DBM alias files from
-**                             the text format files.
-**             -Cfilename      Use alternate configuration file.
-**             -Afilename      Use alternate alias file.
-**             -DXvalue        Define macro X to have value.
-**             -bv             Verify addresses only.
-**             -bd             Run as a daemon.  Berkeley 4.2 only.
-**             -bf             Fork after address verification.
-**             -bq             Queue up for later delivery.
-**             -ba             Process mail completely.
-**
-**     Return Codes:
-**             As defined in <sysexits.h>.
-**
-**             These codes are actually returned from the auxiliary
-**             mailers; it is their responsibility to make them
-**             correct.
-**
-**     Compilation Flags:
-**             LOG -- if set, everything is logged.
+**             See the associated documentation for details.
 **
 **     Author:
 **
 **     Author:
-**             Eric Allman, UCB/INGRES
+**             Eric Allman, UCB/INGRES (until 10/81)
+**                          Britton-Lee, Inc., purveyors of fine
+**                             database computers (from 11/81)
+**             The support of the INGRES Project and Britton-Lee is
+**                     gratefully acknowledged.  Britton-Lee in
+**                     particular had absolutely nothing to gain from
+**                     my involvement in this project.
 */
 
 
 */
 
 
@@ -108,7 +42,8 @@ SCCSID(@(#)main.c    3.95            %G%);
 
 
 int            NextMailer = 0; /* "free" index into Mailer struct */
 
 
 int            NextMailer = 0; /* "free" index into Mailer struct */
-static char    *FullName;      /* sender's full name */
+char           *FullName;      /* sender's full name */
+ENVELOPE       BlankEnvelope;  /* a "blank" envelope */
 ENVELOPE       MainEnvelope;   /* the envelope around the basic letter */
 
 #ifdef DAEMON
 ENVELOPE       MainEnvelope;   /* the envelope around the basic letter */
 
 #ifdef DAEMON
@@ -127,73 +62,192 @@ main(argc, argv)
        char **argv;
 {
        register char *p;
        char **argv;
 {
        register char *p;
+       char **av;
        char *locname;
        extern int finis();
        extern char Version[];
        char *from;
        typedef int (*fnptr)();
        char *locname;
        extern int finis();
        extern char Version[];
        char *from;
        typedef int (*fnptr)();
+       STAB *st;
        register int i;
        register int i;
+       bool readconfig = FALSE;
        bool safecf = TRUE;             /* this conf file is sys default */
        bool safecf = TRUE;             /* this conf file is sys default */
-       char ibuf[30];                  /* holds HostName */
        bool queuemode = FALSE;         /* process queue requests */
        bool queuemode = FALSE;         /* process queue requests */
-       bool aliasinit = FALSE;
+       static bool reenter = FALSE;
+       char jbuf[30];                  /* holds HostName */
        extern bool safefile();
        extern bool safefile();
-       STAB *st;
        extern time_t convtime();
        extern putheader(), putbody();
        extern time_t convtime();
        extern putheader(), putbody();
+       extern ENVELOPE *newenvelope();
+       extern intsig();
+       extern char **myhostname();
+
+       /*
+       **  Check to see if we reentered.
+       **      This would normally happen if e_putheader or e_putbody
+       **      were NULL when invoked.
+       */
+
+       if (reenter)
+       {
+               syserr("main: reentered!");
+               abort();
+       }
+       reenter = TRUE;
        extern ADDRESS *recipient();
        bool canrename;
 
        extern ADDRESS *recipient();
        bool canrename;
 
+       /*
+       **  Be sure we have enough file descriptors.
+       */
+
+       for (i = 3; i < 20; i++)
+               (void) close(i);
+       errno = 0;
+
+       /*
+       **  Do a quick prescan of the argument list.
+       **      We do this to find out if we can potentially thaw the
+       **      configuration file.  If not, we do the thaw now so that
+       **      the argument processing applies to this run rather than
+       **      to the run that froze the configuration.
+       */
+
        argv[argc] = NULL;
        argv[argc] = NULL;
+       av = argv;
+       while (*++av != NULL)
+       {
+               if (strncmp(*av, "-C", 2) == 0 || strncmp(*av, "-bz", 3) == 0)
+                       break;
+       }
+       if (*av == NULL)
+               readconfig = !thaw(FreezeFile);
+
+       /*
+       **  Now do basic initialization
+       */
+
        InChannel = stdin;
        OutChannel = stdout;
        InChannel = stdin;
        OutChannel = stdout;
-       MsgId = "<none>";
        if (signal(SIGINT, SIG_IGN) != SIG_IGN)
        if (signal(SIGINT, SIG_IGN) != SIG_IGN)
-               (void) signal(SIGINT, finis);
+               (void) signal(SIGINT, intsig);
        if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
        if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
-               (void) signal(SIGHUP, finis);
-       (void) signal(SIGTERM, finis);
+               (void) signal(SIGHUP, intsig);
+       (void) signal(SIGTERM, intsig);
+       (void) signal(SIGPIPE, SIG_IGN);
        OldUmask = umask(0);
        OldUmask = umask(0);
-       Mode = MD_DEFAULT;
+       OpMode = MD_DELIVER;
        MotherPid = getpid();
 # ifndef V6
        FullName = getenv("NAME");
 # endif V6
 
        MotherPid = getpid();
 # ifndef V6
        FullName = getenv("NAME");
 # endif V6
 
-       /* set up the main envelope */
-       MainEnvelope.e_puthdr = putheader;
-       MainEnvelope.e_putbody = putbody;
-       CurEnv = &MainEnvelope;
+       /* set up the blank envelope */
+       BlankEnvelope.e_puthdr = putheader;
+       BlankEnvelope.e_putbody = putbody;
+       BlankEnvelope.e_xfp = NULL;
+       CurEnv = &BlankEnvelope;
+
+       /* make sure we have a clean slate */
+       closeall();
 
 # ifdef LOG
 
 # ifdef LOG
-       openlog("sendmail", 0);
+       openlog("sendmail", LOG_PID);
 # endif LOG
 # endif LOG
-       openxscrpt();
        errno = 0;
        from = NULL;
        errno = 0;
        from = NULL;
-       CurEnv->e_oldstyle = TRUE;
-       NoConnect = TRUE;
+
+       /* initialize some macros, etc. */
        initmacros();
 
        initmacros();
 
+       /* hostname */
+       av = myhostname(jbuf);
+       if (jbuf[0] != '\0')
+       {
+               p = newstr(jbuf);
+               define('w', p, CurEnv);
+               setclass('w', p);
+       }
+       while (av != NULL && *av != NULL)
+               setclass('w', *av++);
+
+       /* version */
+       define('v', Version, CurEnv);
+
+       /* current time */
+       define('b', arpadate(NULL), CurEnv);
+
        /*
        ** Crack argv.
        */
 
        /*
        ** Crack argv.
        */
 
-       while (--argc > 0 && (p = *++argv)[0] == '-')
+       av = argv;
+       p = rindex(*av, '/');
+       if (p++ == NULL)
+               p = *av;
+       if (strcmp(p, "newaliases") == 0)
+               OpMode = MD_INITALIAS;
+       else if (strcmp(p, "mailq") == 0)
+               OpMode = MD_PRINT;
+       while ((p = *++av) != NULL && p[0] == '-')
        {
                switch (p[1])
                {
        {
                switch (p[1])
                {
-                 case 'r':     /* obsolete -f flag */
+                 case 'b':     /* operations mode */
+                       switch (p[2])
+                       {
+                         case MD_DAEMON:
+# ifndef DAEMON
+                               syserr("Daemon mode not implemented");
+                               break;
+# endif DAEMON
+                         case MD_SMTP:
+# ifndef SMTP
+                               syserr("I don't speak SMTP");
+                               break;
+# endif SMTP
+                         case MD_ARPAFTP:
+                         case MD_DELIVER:
+                         case MD_VERIFY:
+                         case MD_TEST:
+                         case MD_INITALIAS:
+                         case MD_PRINT:
+                         case MD_FREEZE:
+                               OpMode = p[2];
+                               break;
+
+                         default:
+                               syserr("Invalid operation mode %c", p[2]);
+                               break;
+                       }
+                       break;
+
+                 case 'C':     /* select configuration file */
+                       ConfFile = &p[2];
+                       if (ConfFile[0] == '\0')
+                               ConfFile = "sendmail.cf";
+                       safecf = FALSE;
+                       break;
+
+# ifdef DEBUG
+                 case 'd':     /* debug */
+                       tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
+                       tTflag(&p[2]);
+                       setbuf(stdout, (char *) NULL);
+                       printf("Version %s\n", Version);
+                       break;
+# endif DEBUG
+
                  case 'f':     /* from address */
                  case 'f':     /* from address */
+                 case 'r':     /* obsolete -f flag */
                        p += 2;
                        p += 2;
-                       if (*p == '\0')
+                       if (*p == '\0' && ((p = *++av) == NULL || *p == '-'))
                        {
                        {
-                               p = *++argv;
-                               if (--argc <= 0 || *p == '-')
+                               p = *++av;
+                               if (p == NULL || *p == '-')
                                {
                                        syserr("No \"from\" person");
                                {
                                        syserr("No \"from\" person");
-                                       argc++;
-                                       argv--;
+                                       av--;
                                        break;
                                }
                        }
                                        break;
                                }
                        }
@@ -207,222 +261,102 @@ main(argc, argv)
 
                  case 'F':     /* set full name */
                        p += 2;
 
                  case 'F':     /* set full name */
                        p += 2;
-                       if (*p == '\0')
+                       if (*p == '\0' && ((p = *++av) == NULL || *p == '-'))
                        {
                        {
-                               p = *++argv;
-                               if (--argc <= 0 || *p == '-')
-                               {
-                                       syserr("Bad -F flag");
-                                       argc++;
-                                       argv--;
-                                       break;
-                               }
+                               syserr("Bad -F flag");
+                               av--;
+                               break;
                        }
                        FullName = p;
                        break;
 
                  case 'h':     /* hop count */
                        p += 2;
                        }
                        FullName = p;
                        break;
 
                  case 'h':     /* hop count */
                        p += 2;
-                       if (*p == '\0')
+                       if (*p == '\0' && ((p = *++av) == NULL || !isdigit(*p)))
                        {
                        {
-                               p = *++argv;
-                               if (--argc <= 0 || *p < '0' || *p > '9')
-                               {
-                                       syserr("Bad hop count (%s)", p);
-                                       argc++;
-                                       argv--;
-                                       break;
-                               }
-                       }
-                       HopCount = atoi(p);
-                       break;
-
-                 case 'e':     /* error message disposition */
-                       switch (p[2])
-                       {
-                         case 'p':     /* print errors normally */
-                               break;  /* (default) */
-
-                         case 'q':     /* be silent about it */
-                               (void) freopen("/dev/null", "w", stdout);
-                               break;
-
-                         case 'm':     /* mail back */
-                               MailBack = TRUE;
-                               HoldErrs = TRUE;
-                               break;
-
-                         case 'e':     /* do berknet error processing */
-                               BerkNet = TRUE;
-                               HoldErrs = TRUE;
-                               break;
-
-                         case 'w':     /* write back (or mail) */
-                               WriteBack = TRUE;
-                               HoldErrs = TRUE;
+                               syserr("Bad hop count (%s)", p);
+                               av--;
                                break;
                        }
                                break;
                        }
-                       break;
-
-# ifdef DEBUG
-                 case 'd':     /* debug */
-                       tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
-                       tTflag(&p[2]);
-                       setbuf(stdout, (char *) NULL);
-                       printf("Version %s\n", Version);
-                       break;
-
-                 case 'M':     /* redefine internal macro */
-                       define(p[2], &p[3]);
-                       break;
-# endif DEBUG
-
-                 case 'L':     /* logging */
-                       LogLevel = atoi(&p[2]);
-                       break;
-
-                 case 'C':     /* select configuration file */
-                       if (p[2] == '\0')
-                               ConfFile = "sendmail.cf";
-                       else
-                               ConfFile = &p[2];
-                       safecf = FALSE;
-                       break;
-
-                 case 'A':     /* select alias file */
-                       if (p[2] == '\0')
-                               AliasFile = "aliases";
-                       else
-                               AliasFile = &p[2];
-                       break;
-
-                 case 'Q':     /* select queue dir */
-                       if (p[2] == '\0')
-                               QueueDir = "mqueue";
-                       else
-                       {
-                               if (strlen(&p[2]) > 50)
-                                       syserr("Absurd length Queue path");
-                               else
-                                       QueueDir = &p[2];
-                       }
-                       break;
-
-                 case 'T':     /* set timeout interval */
-                       TimeOut = convtime(&p[2]);
+                       CurEnv->e_hopcount = atoi(p);
                        break;
                
                  case 'n':     /* don't alias */
                        NoAlias = TRUE;
                        break;
 
                        break;
                
                  case 'n':     /* don't alias */
                        NoAlias = TRUE;
                        break;
 
-# ifdef DBM
-                 case 'I':     /* initialize alias DBM file */
-                       aliasinit = TRUE;
-                       Verbose = TRUE;
-                       break;
-# endif DBM
-
-                 case 'm':     /* send to me too */
-                       MeToo = TRUE;
-                       break;
-
-                 case 'i':     /* don't let dot stop me */
-                       IgnrDot = TRUE;
-                       break;
-
-                 case 'a':     /* arpanet format */
-                       ArpaMode = TRUE;
-                       if (p[2] == 's')
-                       {
-                               /* running smtp */
-# ifdef SMTP
-                               Smtp = TRUE;
-# else SMTP
-                               syserr("I don't speak SMTP");
-# endif SMTP
-                       }
+                 case 'o':     /* set option */
+                       setoption(p[2], &p[3], FALSE, TRUE);
                        break;
 
                        break;
 
+                 case 'q':     /* run queue files at intervals */
 # ifdef QUEUE
 # ifdef QUEUE
-                 case 'c':     /* connect to non-local mailers */
-                       NoConnect = FALSE;
-                       break;
+                       queuemode = TRUE;
+                       QueueIntvl = convtime(&p[2]);
+# else QUEUE
+                       syserr("I don't know about queues");
 # endif QUEUE
 # endif QUEUE
-               
-                 case 's':     /* save From lines in headers */
-                       SaveFrom = TRUE;
-                       break;
-
-                 case 'v':     /* give blow-by-blow description */
-                       Verbose = TRUE;
-                       NoConnect = FALSE;
                        break;
 
                  case 't':     /* read recipients from message */
                        GrabTo = TRUE;
                        break;
 
                        break;
 
                  case 't':     /* read recipients from message */
                        GrabTo = TRUE;
                        break;
 
-                 case 'b':     /* operations mode */
-                       Mode = p[2];
-                       switch (Mode)
-                       {
-                         case MD_DAEMON:       /* run as a daemon */
-#ifdef DAEMON
-                               ArpaMode = Smtp = TRUE;
-#else DAEMON
-                               syserr("Daemon mode not implemented");
-#endif DAEMON
-                               break;
-
-                         case '\0':    /* default: do full delivery */
-                               Mode = MD_DEFAULT;
-                               /* fall through....... */
-
-                         case MD_DELIVER:      /* do everything (default) */
-                         case MD_FORK:         /* fork after verification */
-                         case MD_QUEUE:        /* queue only */
-                         case MD_VERIFY:       /* verify only */
-                               break;
-
-                         default:
-                               syserr("Unknown operation mode -b%c", Mode);
-                               exit(EX_USAGE);
-                       }
-                       break;
-
-                 case 'q':     /* run queue files at intervals */
-# ifdef QUEUE
-                       queuemode = TRUE;
-                       QueueIntvl = convtime(&p[2]);
-# else QUEUE
-                       syserr("I don't know about queues");
-# endif QUEUE
+                       /* compatibility flags */
+                 case 'c':     /* connect to non-local mailers */
+                 case 'e':     /* error message disposition */
+                 case 'i':     /* don't let dot stop me */
+                 case 'm':     /* send to me too */
+                 case 'T':     /* set timeout interval */
+                 case 'v':     /* give blow-by-blow description */
+                       setoption(p[1], &p[2], FALSE, TRUE);
                        break;
 
                        break;
 
-                 case 'o':     /* take new-style headers (with commas) */
-                       CurEnv->e_oldstyle = FALSE;
+                 case 's':     /* save From lines in headers */
+                       setoption('f', &p[2], FALSE, TRUE);
                        break;
 
                        break;
 
-                 default:
-                       /* at Eric Schmidt's suggestion, this will not be an error....
-                       syserr("Unknown flag %s", p);
-                       ... seems that upward compatibility will be easier. */
+# ifdef DBM
+                 case 'I':     /* initialize alias DBM file */
+                       OpMode = MD_INITALIAS;
                        break;
                        break;
+# endif DBM
                }
        }
 
        /*
                }
        }
 
        /*
-       **  Read system control file.
+       **  Do basic initialization.
+       **      Read system control file.
        **      Extract special fields for local use.
        */
 
        **      Extract special fields for local use.
        */
 
-       readcf(ConfFile, safecf);
-       initsys();
+       if (!safecf || OpMode == MD_FREEZE || readconfig)
+               readcf(ConfFile, safecf);
+
+       switch (OpMode)
+       {
+         case MD_FREEZE:
+               freeze(FreezeFile);
+               exit(EX_OK);
+
+         case MD_INITALIAS:
+               Verbose = TRUE;
+               break;
+       }
+
+       /* do heuristic mode adjustment */
+       if (Verbose)
+       {
+               /* turn off noconnect option */
+               setoption('c', "F", TRUE, FALSE);
+
+               /* turn on interactive delivery */
+               setoption('d', "", TRUE, FALSE);
+       }
 
        /* our name for SMTP codes */
 
        /* our name for SMTP codes */
-       expand("$i", ibuf, &ibuf[sizeof ibuf - 1], CurEnv);
-       HostName = ibuf;
+       expand("$j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv);
+       HostName = jbuf;
 
        /* the indices of local and program mailers */
        st = stab("local", ST_MAILER, ST_FIND);
 
        /* the indices of local and program mailers */
        st = stab("local", ST_MAILER, ST_FIND);
@@ -436,15 +370,36 @@ main(argc, argv)
        else
                ProgMailer = st->s_mailer;
 
        else
                ProgMailer = st->s_mailer;
 
+       /* operate in queue directory */
+       if (chdir(QueueDir) < 0)
+       {
+               syserr("cannot chdir(%s)", QueueDir);
+               exit(EX_SOFTWARE);
+       }
+
+       /*
+       **  If printing the queue, go off and do that.
+       */
+
+       if (OpMode == MD_PRINT)
+       {
+#ifdef QUEUE
+               dropenvelope(CurEnv);
+               printqueue();
+               exit(EX_OK);
+#else QUEUE
+               usrerr("No queue to print");
+               finis();
+#endif QUEUE
+       }
+
        /*
        **  Initialize aliases.
        */
 
        /*
        **  Initialize aliases.
        */
 
-       initaliases(AliasFile, aliasinit);
-# ifdef DBM
-       if (aliasinit)
+       initaliases(AliasFile, OpMode == MD_INITALIAS);
+       if (OpMode == MD_INITALIAS)
                exit(EX_OK);
                exit(EX_OK);
-# endif DBM
 
 # ifdef DEBUG
        if (tTd(0, 15))
 
 # ifdef DEBUG
        if (tTd(0, 15))
@@ -454,16 +409,87 @@ main(argc, argv)
                for (i = 0; i < MAXMAILERS; i++)
                {
                        register struct mailer *m = Mailer[i];
                for (i = 0; i < MAXMAILERS; i++)
                {
                        register struct mailer *m = Mailer[i];
+                       int j;
 
                        if (m == NULL)
                                continue;
 
                        if (m == NULL)
                                continue;
-                       printf("mailer %d: %s %s %lo %s\n", i, m->m_name,
-                              m->m_mailer, m->m_flags, m->m_from);
+                       printf("mailer %d (%s): P=%s S=%d R=%d M=%ld F=", i, m->m_name,
+                               m->m_mailer, m->m_s_rwset, m->m_r_rwset,
+                               m->m_maxsize);
+                       for (j = '\0'; j <= '\177'; j++)
+                               if (bitnset(j, m->m_flags))
+                                       putchar(j);
+                       printf(" E=");
+                       xputs(m->m_eol);
+                       printf("\n");
                }
        }
 # endif DEBUG
 
                }
        }
 # endif DEBUG
 
-#ifdef DAEMON
+       /*
+       **  Switch to the main envelope.
+       */
+
+       CurEnv = newenvelope(&MainEnvelope);
+       MainEnvelope.e_flags = BlankEnvelope.e_flags;
+
+       /*
+       **  If test mode, read addresses from stdin and process.
+       */
+
+       if (OpMode == MD_TEST)
+       {
+               char buf[MAXLINE];
+
+               printf("ADDRESS TEST MODE\nEnter <ruleset> <address>\n");
+               for (;;)
+               {
+                       register char **pvp;
+                       char *q;
+                       extern char **prescan();
+                       extern char *DelimChar;
+
+                       printf("> ");
+                       fflush(stdout);
+                       if (fgets(buf, sizeof buf, stdin) == NULL)
+                               finis();
+                       for (p = buf; isspace(*p); *p++)
+                               continue;
+                       q = p;
+                       while (*p != '\0' && !isspace(*p))
+                               p++;
+                       if (*p == '\0')
+                               continue;
+                       *p = '\0';
+                       do
+                       {
+                               pvp = prescan(++p, ',');
+                               if (pvp == NULL)
+                                       continue;
+                               rewrite(pvp, 3);
+                               p = q;
+                               while (*p != '\0')
+                               {
+                                       rewrite(pvp, atoi(p));
+                                       while (*p != '\0' && *p++ != ',')
+                                               continue;
+                               }
+                       } while (*(p = DelimChar) != '\0');
+               }
+       }
+
+# ifdef QUEUE
+       /*
+       **  If collecting stuff from the queue, go start doing that.
+       */
+
+       if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0)
+       {
+               runqueue(FALSE);
+               finis();
+       }
+# endif QUEUE
+
        /*
        **  If a daemon, wait for a request.
        **      getrequests will always return in a child.
        /*
        **  If a daemon, wait for a request.
        **      getrequests will always return in a child.
@@ -473,7 +499,7 @@ main(argc, argv)
        **              during startup.
        */
 
        **              during startup.
        */
 
-       if (Mode == MD_DAEMON)
+       if (OpMode == MD_DAEMON || QueueIntvl != 0)
        {
                if (!tTd(0, 1))
                {
        {
                if (!tTd(0, 1))
                {
@@ -488,78 +514,60 @@ main(argc, argv)
                        MotherPid = getpid();
 
                        /* disconnect from our controlling tty */
                        MotherPid = getpid();
 
                        /* disconnect from our controlling tty */
-                       i = open("/dev/tty", 2);
-                       if (i >= 0)
-                       {
-                               (void) ioctl(i, TIOCNOTTY, 0);
-                               (void) close(i);
-                       }
-                       errno = 0;
+                       disconnect(TRUE);
                }
                }
+
 # ifdef QUEUE
                if (queuemode)
 # ifdef QUEUE
                if (queuemode)
+               {
                        runqueue(TRUE);
                        runqueue(TRUE);
+                       if (OpMode != MD_DAEMON)
+                               for (;;)
+                                       pause();
+               }
 # endif QUEUE
 # endif QUEUE
-               checkerrors(CurEnv);
+               dropenvelope(CurEnv);
+
+#ifdef DAEMON
                getrequests();
 
                /* at this point we are in a child: reset state */
                getrequests();
 
                /* at this point we are in a child: reset state */
-               FatalErrors = FALSE;
-               openxscrpt();
-               initsys();
-       }
+               OpMode = MD_SMTP;
+               (void) newenvelope(CurEnv);
+               openxscript(CurEnv);
 #endif DAEMON
 #endif DAEMON
+       }
        
 # ifdef SMTP
        /*
        
 # ifdef SMTP
        /*
-       if (Smtp)
-               smtp();
-# endif SMTP
-
-# ifdef QUEUE
-       /*
-       **  If collecting stuff from the queue, go start doing that.
+       **  If running SMTP protocol, start collecting and executing
+       **  commands.  This will never return.
        */
 
        */
 
-       if (queuemode && Mode != MD_DAEMON)
-       {
-               runqueue(FALSE);
-               finis();
-       }
-# endif QUEUE
+       if (OpMode == MD_SMTP)
+               smtp();
+# endif SMTP
 
        /*
 
        /*
-       **  Set the sender
+       **  Do basic system initialization and set the sender
        */
 
        */
 
+       initsys();
        setsender(from);
 
        setsender(from);
 
-       if (Mode != MD_DAEMON && argc <= 0 && !GrabTo)
+       if (OpMode != MD_ARPAFTP && *av == NULL && !GrabTo)
        {
                usrerr("Usage: /etc/sendmail [flags] addr...");
                finis();
        }
        {
                usrerr("Usage: /etc/sendmail [flags] addr...");
                finis();
        }
-
-       /*
-       **  Process Hop count.
-       **      The Hop count tells us how many times this message has
-       **      been processed by sendmail.  If it exceeds some
-       **      fairly large threshold, then we assume that we have
-       **      an infinite forwarding loop and die.
-       */
-
-       if (++HopCount > MAXHOP)
-               syserr("Infinite forwarding loop (%s->%s)", CurEnv->e_from.q_paddr, *argv);
+       if (OpMode == MD_VERIFY)
+               SendMode = SM_VERIFY;
 
        /*
        **  Scan argv and deliver the message to everyone.
 
        /*
        **  Scan argv and deliver the message to everyone.
-       **      Actually, suppress delivery if we are taking To:
-       **      lines from the message.
        */
 
        */
 
-       if (GrabTo)
-               DontSend = TRUE;
-       sendtoargv(argv);
+       sendtoargv(av);
 
        /* if we have had errors sofar, arrange a meaningful exit stat */
        if (Errors > 0 && ExitStat == EX_OK)
 
        /* if we have had errors sofar, arrange a meaningful exit stat */
        if (Errors > 0 && ExitStat == EX_OK)
@@ -569,211 +577,36 @@ main(argc, argv)
        **  Read the input mail.
        */
 
        **  Read the input mail.
        */
 
-       DontSend = FALSE;
        CurEnv->e_to = NULL;
        CurEnv->e_to = NULL;
-       if (Mode != MD_VERIFY || GrabTo)
+       if (OpMode != MD_VERIFY || GrabTo)
                collect(FALSE);
        errno = 0;
 
                collect(FALSE);
        errno = 0;
 
-       /*
-       **  If we don't want to wait around for actual delivery, this
-       **  is a good time to fork off.
-       **      We have examined what we can without doing actual
-       **              delivery, so we will inform our caller of
-       **              whatever we can now.
-       **      Since the parent process will go away immediately,
-       **              the child will be caught by init.
-       **      If the fork fails, we will just continue in the
-       **              parent; this is perfectly safe, albeit
-       **              slower than it must be.
-       **      If we have errors sofar, this seems like a good time
-       **              to dispose of them.
-       */
-
-       if (ExitStat != EX_OK && Mode != MD_VERIFY)
-               finis();
-
-       if (Mode == MD_FORK)
-       {
-               if (fork() > 0)
-               {
-                       /* parent -- quit */
-                       exit(ExitStat);
-               }
-       }
-       else if (Mode == MD_QUEUE)
-       {
-               queueup(CurEnv, TRUE);
-               exit(ExitStat);
-       }
-
-       initsys();
-
        /* collect statistics */
        /* collect statistics */
-       Stat.stat_nf[CurEnv->e_from.q_mailer->m_mno]++;
-       Stat.stat_bf[CurEnv->e_from.q_mailer->m_mno] += kbytes(CurEnv->e_msgsize);
-
-       /*
-       **  Arrange that the person who is sending the mail
-       **  will not be expanded (unless explicitly requested).
-       */
+       if (OpMode != MD_VERIFY)
+               markstats(CurEnv, (ADDRESS *) NULL);
 
 # ifdef DEBUG
        if (tTd(1, 1))
                printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
 # endif DEBUG
 
 
 # ifdef DEBUG
        if (tTd(1, 1))
                printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
 # endif DEBUG
 
-       CurEnv->e_from.q_flags |= QDONTSEND;
-       if (!MeToo)
-               recipient(&CurEnv->e_from, &CurEnv->e_sendqueue);
-       CurEnv->e_to = NULL;
-
        /*
        **  Actually send everything.
        **      If verifying, just ack.
        */
 
        /*
        **  Actually send everything.
        **      If verifying, just ack.
        */
 
-       sendall(CurEnv, Mode == MD_VERIFY);
+       CurEnv->e_from.q_flags |= QDONTSEND;
+       CurEnv->e_to = NULL;
+       sendall(CurEnv, SendMode);
 
        /*
        ** All done.
        */
 
 
        /*
        ** All done.
        */
 
-       CurEnv->e_to = NULL;
-       if (Mode != MD_VERIFY)
-               poststats(StatFile);
        finis();
 }
 \f/*
        finis();
 }
 \f/*
-**  SETFROM -- set the person who this message is from
-**
-**     Under certain circumstances allow the user to say who
-**     s/he is (using -f or -r).  These are:
-**     1.  The user's uid is zero (root).
-**     2.  The user's login name is "network" (mail from
-**         a network server).
-**     3.  The user's login name is "uucp" (mail from the
-**         uucp network).
-**     4.  The address the user is trying to claim has a
-**         "!" character in it (since #3 doesn't do it for
-**         us if we are dialing out).
-**     A better check to replace #3 & #4 would be if the
-**     effective uid is "UUCP" -- this would require me
-**     to rewrite getpwent to "grab" uucp as it went by,
-**     make getname more nasty, do another passwd file
-**     scan, or compile the UID of "UUCP" into the code,
-**     all of which are reprehensible.
-**
-**     Assuming all of these fail, we figure out something
-**     ourselves.
-**
-**     Parameters:
-**             from -- the person it is from.
-**             realname -- the actual person executing sendmail.
-**                     If NULL, then take whoever we previously
-**                     thought was the from person.
-**
-**     Returns:
-**             none.
-**
-**     Side Effects:
-**             sets sendmail's notion of who the from person is.
-*/
-
-setfrom(from, realname)
-       char *from;
-       char *realname;
-{
-       register char **pvp;
-       char frombuf[MAXNAME];
-       extern char **prescan();
-       extern char *index();
-       extern char *macvalue();
-
-       if (realname == NULL)
-               realname = CurEnv->e_from.q_paddr;
-
-# ifdef DEBUG
-       if (tTd(1, 2))
-               printf("setfrom(%s, %s)\n", from, realname);
-# endif DEBUG
-
-       /*
-       **  Do validation to determine whether this user is allowed
-       **  to change the sender name.
-       */
-
-       if (from != NULL)
-       {
-               if (strcmp(realname, "network") != 0 &&
-                   strcmp(realname, "uucp") != 0 &&
-                   strcmp(realname, "daemon") != 0 &&
-# ifdef DEBUG
-                   (!tTd(1, 1) || getuid() != geteuid()) &&
-# endif DEBUG
-                   index(from, '!') == NULL && getuid() != 0)
-               {
-                       /* network sends -r regardless (why why why?) */
-                       /* syserr("%s, you cannot use the -f flag", realname); */
-                       from = NULL;
-               }
-       }
-
-       /*
-       **  Parse the sender name.
-       **      Arrange to send return messages to the same person.
-       **      Set up some environment info.
-       */
-
-       SuprErrs = TRUE;
-       if (from == NULL || parse(from, &CurEnv->e_from, 1) == NULL)
-       {
-               from = newstr(realname);
-               (void) parse(from, &CurEnv->e_from, 1);
-       }
-       else
-               FromFlag = TRUE;
-       CurEnv->e_returnto = &CurEnv->e_from;
-       SuprErrs = FALSE;
-       CurEnv->e_from.q_uid = getuid();
-       CurEnv->e_from.q_gid = getgid();
-# ifndef V6
-       CurEnv->e_from.q_home = getenv("HOME");
-# endif V6
-       if (CurEnv->e_from.q_uid != 0)
-       {
-               DefUid = CurEnv->e_from.q_uid;
-               DefGid = CurEnv->e_from.q_gid;
-       }
-
-       /*
-       **  Set up the $r and $s macros to show who it came from.
-       */
-
-       if (macvalue('s') == NULL && CurEnv->e_from.q_host != NULL &&
-           CurEnv->e_from.q_host[0] != '\0')
-       {
-               define('s', CurEnv->e_from.q_host);
-
-               /* should determine network type here */
-       }
-
-       /*
-       **  Rewrite the from person to dispose of possible implicit
-       **      links in the net.
-       */
-
-       pvp = prescan(from, '\0');
-       if (pvp == NULL)
-       {
-               syserr("cannot prescan from (%s)", from);
-               finis();
-       }
-       rewrite(pvp, 1);
-       cataddr(pvp, frombuf, sizeof frombuf);
-       define('f', newstr(frombuf));
-}
-\f/*
 **  FINIS -- Clean up and exit.
 **
 **     Parameters:
 **  FINIS -- Clean up and exit.
 **
 **     Parameters:
@@ -788,340 +621,313 @@ setfrom(from, realname)
 
 finis()
 {
 
 finis()
 {
-       CurEnv = &MainEnvelope;
-
 # ifdef DEBUG
        if (tTd(2, 1))
 # ifdef DEBUG
        if (tTd(2, 1))
-       {
-               printf("\n====finis: stat %d sendreceipt %d FatalErrors %d\n",
-                    ExitStat, CurEnv->e_sendreceipt, FatalErrors);
-       }
+               printf("\n====finis: stat %d e_flags %o\n", ExitStat, CurEnv->e_flags);
 # endif DEBUG
 
 # endif DEBUG
 
-       /*
-       **  Send back return receipts as requested.
-       */
-
-       if (CurEnv->e_sendreceipt && ExitStat == EX_OK)
-               (void) returntosender("Return receipt", &CurEnv->e_from, FALSE);
-
-       /*
-       **  Arrange to return errors or queue up as appropriate.
-       **      If we are running a queue file and exiting abnormally,
-       **              be sure we save the queue file.
-       **      This clause will arrange to return error messages.
-       */
-
-       if (ControlFile != NULL)
-               CurEnv->e_queueup = TRUE;
-       checkerrors(CurEnv);
+       /* clean up temp files */
+       CurEnv->e_to = NULL;
+       dropenvelope(CurEnv);
 
 
-       /*
-       **  Now clean up temp files and exit.
-       */
+       /* post statistics */
+       poststats(StatFile);
 
 
-       if (Transcript != NULL)
-               xunlink(Transcript);
-       if (CurEnv->e_df != NULL)
-               xunlink(CurEnv->e_df);
-       if (ControlFile != NULL)
-               xunlink(ControlFile);
+       /* and exit */
+# ifdef LOG
+       if (LogLevel > 11)
+               syslog(LOG_DEBUG, "finis, pid=%d", getpid());
+# endif LOG
+       if (ExitStat == EX_TEMPFAIL)
+               ExitStat = EX_OK;
        exit(ExitStat);
 }
 \f/*
        exit(ExitStat);
 }
 \f/*
-**  OPENXSCRPT -- Open transcript file
+**  INTSIG -- clean up on interrupt
 **
 **
-**     Creates a transcript file for possible eventual mailing or
-**     sending back.
+**     This just arranges to exit.  It pessimises in that it
+**     may resend a message.
 **
 **     Parameters:
 **
 **     Parameters:
-**             none
+**             none.
 **
 **     Returns:
 **
 **     Returns:
-**             none
+**             none.
 **
 **     Side Effects:
 **
 **     Side Effects:
-**             Turns the standard output into a special file
-**                     somewhere.
+**             Unlocks the current job.
 */
 
 */
 
-openxscrpt()
+intsig()
 {
 {
-       extern char *mktemp();
-       register char *p;
-
-       p = newstr(XcriptFile);
-       (void) mktemp(p);
-       Xscript = fopen(p, "w");
-       if (Xscript == NULL)
-       {
-               Xscript = stdout;
-               syserr("Can't create %s", p);
-       }
-       Transcript = p;
-       (void) chmod(p, 0600);
+       FileName = NULL;
+       unlockqueue(CurEnv);
+       exit(EX_OK);
 }
 \f/*
 }
 \f/*
-**  SETSENDER -- set sendmail's idea of the sender.
+**  INITMACROS -- initialize the macro system
+**
+**     This just involves defining some macros that are actually
+**     used internally as metasymbols to be themselves.
 **
 **     Parameters:
 **
 **     Parameters:
-**             from -- the person we would like to believe this
-**                     is from.
+**             none.
 **
 **     Returns:
 **             none.
 **
 **     Side Effects:
 **
 **     Returns:
 **             none.
 **
 **     Side Effects:
-**             Sets the idea of the sender.
+**             initializes several macros to be themselves.
 */
 
 */
 
-setsender(from)
-       char *from;
+struct metamac
 {
 {
-       register char *p;
-       extern char *getlogin();
-       register struct passwd *pw;
-       char *realname;
-       char cfbuf[40];
-       bool nofullname;
-       extern char *macvalue();
-
-       /*
-       **  Figure out the real user executing us.
-       **      Getlogin can return errno != 0 on non-errors.
-       */
-
-       if (!Smtp && !QueueRun)
-       {
-               errno = 0;
-               p = getlogin();
-               errno = 0;
-               nofullname = (from != NULL);
-       }
-       else
-       {
-               p = from;
-               nofullname = FALSE;
-       }
-       if (p != NULL && p[0] != '\0')
-       {
-               extern struct passwd *getpwnam();
+       char    metaname;
+       char    metaval;
+};
 
 
-               pw = getpwnam(p);
-               if (pw == NULL)
-               {
-                       if (!Smtp && !QueueRun)
-                               syserr("Who are you? (name=%s)", p);
-                       p = NULL;
-               }
-       }
-       if (p == NULL || p[0] == '\0')
-       {
-               extern struct passwd *getpwuid();
-               int uid;
-
-               nofullname = TRUE;
-               uid = getruid();
-               pw = getpwuid(uid);
-               if (pw == NULL)
-                       syserr("Who are you? (uid=%d)", uid);
-               else
-                       p = pw->pw_name;
-       }
-       if (p == NULL || p[0] == '\0' || pw == NULL)
-               finis();
+struct metamac MetaMacros[] =
+{
+       /* these are important on the LHS */
+       '*', MATCHZANY, '+', MATCHANY,  '-', MATCHONE,  '=', MATCHCLASS,
+       '~', MATCHNCLASS,
 
 
-       realname = p;
+       /* these are RHS metasymbols */
+       '#', CANONNET,  '@', CANONHOST, ':', CANONUSER, '>', CALLSUBR,
 
 
-       /*
-       **  Process passwd file entry.
-       */
+       /* and finally the conditional operations */
+       '?', CONDIF,    '|', CONDELSE,  '.', CONDFI,
 
 
-       /* run user's .mailcf file */
-       define('z', pw->pw_dir);
-       expand("$z/.mailcf", cfbuf, &cfbuf[sizeof cfbuf - 1], CurEnv);
-       if (!nofullname && safefile(cfbuf, getruid(), S_IREAD))
-               readcf(cfbuf, FALSE);
+       '\0'
+};
 
 
-       /* if the user has given fullname already, don't redefine */
-       if (FullName == NULL)
-               FullName = macvalue('x');
+initmacros()
+{
+       register struct metamac *m;
+       char buf[5];
+       register int c;
 
 
-       /* extract full name from passwd file */
-       if (!nofullname && (FullName == NULL || FullName[0] == '\0') &&
-           pw != NULL && pw->pw_gecos != NULL)
+       for (m = MetaMacros; m->metaname != '\0'; m++)
        {
        {
-               char nbuf[MAXNAME];
-
-               fullname(pw, nbuf);
-               if (nbuf[0] != '\0')
-                       FullName = newstr(nbuf);
+               buf[0] = m->metaval;
+               buf[1] = '\0';
+               define(m->metaname, newstr(buf), CurEnv);
+       }
+       buf[0] = MATCHREPL;
+       buf[2] = '\0';
+       for (c = '0'; c <= '9'; c++)
+       {
+               buf[1] = c;
+               define(c, newstr(buf), CurEnv);
        }
        }
-       if (FullName != NULL && FullName[0] != '\0')
-               define('x', FullName);
-
-       setfrom(from, realname);
 }
 \f/*
 }
 \f/*
-**  INITSYS -- initialize instantiation of system
+**  FREEZE -- freeze BSS & allocated memory
 **
 **
-**     In Daemon mode, this is done in the child.
+**     This will be used to efficiently load the configuration file.
 **
 **     Parameters:
 **
 **     Parameters:
-**             none.
+**             freezefile -- the name of the file to freeze to.
 **
 **     Returns:
 **             none.
 **
 **     Side Effects:
 **
 **     Returns:
 **             none.
 **
 **     Side Effects:
-**             Initializes the system macros, some global variables,
-**             etc.  In particular, the current time in various
-**             forms is set.
+**             Writes BSS and malloc'ed memory to freezefile
 */
 
 */
 
-initsys()
+union frz
 {
 {
-       static char cbuf[5];                    /* holds hop count */
-       static char dbuf[30];                   /* holds ctime(tbuf) */
-       static char pbuf[10];                   /* holds pid */
-       static char tbuf[20];                   /* holds "current" time */
-       static char ybuf[10];                   /* holds tty id */
-       register char *p;
-       extern char *ttyname();
-       extern char *arpadate();
-       register struct tm *tm;
-       extern struct tm *gmtime();
-
-       /* convert timeout interval to absolute time */
-       TimeOut -= CurTime;
-       (void) time(&CurTime);
-       TimeOut += CurTime;
-
-       /* process id */
-       (void) sprintf(pbuf, "%d", getpid());
-       define('p', pbuf);
-
-       /* hop count */
-       (void) sprintf(cbuf, "%d", HopCount);
-       define('c', cbuf);
-
-       /* time as integer, unix time, arpa time */
-       tm = gmtime(&CurTime);
-       (void) sprintf(tbuf, "%02d%02d%02d%02d%02d", tm->tm_year, tm->tm_mon,
-                       tm->tm_mday, tm->tm_hour, tm->tm_min);
-       define('t', tbuf);
-       (void) strcpy(dbuf, ctime(&CurTime));
-       *index(dbuf, '\n') = '\0';
-       if (macvalue('d') == NULL)
-               define('d', dbuf);
-       p = newstr(arpadate(dbuf));
-       if (macvalue('a') == NULL)
-               define('a', p);
-       define('b', p);
+       char            frzpad[BUFSIZ]; /* insure we are on a BUFSIZ boundary */
+       struct
+       {
+               time_t  frzstamp;       /* timestamp on this freeze */
+               char    *frzbrk;        /* the current break */
+               char    frzver[252];    /* sendmail version */
+       } frzinfo;
+};
 
 
-       /* version */
-       define('v', Version);
+freeze(freezefile)
+       char *freezefile;
+{
+       int f;
+       union frz fhdr;
+       extern char edata;
+       extern char *sbrk();
+       extern char Version[];
 
 
-       /* tty name */
-       if (macvalue('y') == NULL)
+       if (freezefile == NULL)
+               return;
+
+       /* try to open the freeze file */
+       f = creat(freezefile, FileMode);
+       if (f < 0)
        {
        {
-               p = ttyname(2);
-               if (p != NULL)
-               {
-                       if (rindex(p, '/') != NULL)
-                               p = rindex(p, '/') + 1;
-                       (void) strcpy(ybuf, p);
-                       define('y', ybuf);
-               }
+               syserr("Cannot freeze");
+               errno = 0;
+               return;
        }
        }
+
+       /* build the freeze header */
+       fhdr.frzinfo.frzstamp = curtime();
+       fhdr.frzinfo.frzbrk = sbrk(0);
+       strcpy(fhdr.frzinfo.frzver, Version);
+
+       /* write out the freeze header */
+       if (write(f, (char *) &fhdr, sizeof fhdr) != sizeof fhdr ||
+           write(f, (char *) &edata, fhdr.frzinfo.frzbrk - &edata) !=
+                                       (fhdr.frzinfo.frzbrk - &edata))
+       {
+               syserr("Cannot freeze");
+       }
+
+       /* fine, clean up */
+       (void) close(f);
 }
 \f/*
 }
 \f/*
-**  INITMACROS -- initialize the macro system
-**
-**     This just involves defining some macros that are actually
-**     used internally as metasymbols to be themselves.
+**  THAW -- read in the frozen configuration file.
 **
 **     Parameters:
 **
 **     Parameters:
-**             none.
+**             freezefile -- the name of the file to thaw from.
 **
 **     Returns:
 **
 **     Returns:
-**             none.
+**             TRUE if it successfully read the freeze file.
+**             FALSE otherwise.
 **
 **     Side Effects:
 **
 **     Side Effects:
-**             initializes several macros to be themselves.
+**             reads freezefile in to BSS area.
 */
 
 */
 
-struct metamac
-{
-       char    metaname;
-       char    metaval;
-};
-
-struct metamac MetaMacros[] =
+thaw(freezefile)
+       char *freezefile;
 {
 {
-       /* these are important on the LHS */
-       '+',    MATCHANY,       '-',    MATCHONE,       '=',    MATCHCLASS,
+       int f;
+       union frz fhdr;
+       extern char edata;
+       extern char Version[];
 
 
-       /* these are RHS metasymbols */
-       '#',    CANONNET,       '@',    CANONHOST,      ':',    CANONUSER,
+       if (freezefile == NULL)
+               return (FALSE);
 
 
-       /* and finally the conditional operations */
-       '?',    CONDIF,         '|',    CONDELSE,       '.',    CONDFI,
-
-       '\0'
-};
+       /* open the freeze file */
+       f = open(freezefile, 0);
+       if (f < 0)
+       {
+               errno = 0;
+               return (FALSE);
+       }
 
 
-initmacros()
-{
-       register struct metamac *m;
-       char buf[5];
-       register int c;
+       /* read in the header */
+       if (read(f, (char *) &fhdr, sizeof fhdr) < sizeof fhdr ||
+           strcmp(fhdr.frzinfo.frzver, Version) != 0)
+       {
+               (void) close(f);
+               return (FALSE);
+       }
 
 
-       for (m = MetaMacros; m->metaname != '\0'; m++)
+       /* arrange to have enough space */
+       if (brk(fhdr.frzinfo.frzbrk) < 0)
        {
        {
-               buf[0] = m->metaval;
-               buf[1] = '\0';
-               define(m->metaname, newstr(buf));
+               syserr("Cannot break to %x", fhdr.frzinfo.frzbrk);
+               (void) close(f);
+               return (FALSE);
        }
        }
-       buf[0] = MATCHREPL;
-       buf[2] = '\0';
-       for (c = '0'; c <= '9'; c++)
+
+       /* now read in the freeze file */
+       if (read(f, (char *) &edata, fhdr.frzinfo.frzbrk - &edata) !=
+                                       (fhdr.frzinfo.frzbrk - &edata))
        {
        {
-               buf[1] = c;
-               define(c, newstr(buf));
+               /* oops!  we have trashed memory..... */
+               write(2, "Cannot read freeze file\n", 24);
+               _exit(EX_SOFTWARE);
        }
        }
+
+       (void) close(f);
+       return (TRUE);
 }
 \f/*
 }
 \f/*
-**  NEWENVELOPE -- allocate a new envelope
-**
-**     Supports inheritance.
+**  DISCONNECT -- remove our connection with any foreground process
 **
 **     Parameters:
 **
 **     Parameters:
-**             e -- the new envelope to fill in.
+**             fulldrop -- if set, we should also drop the controlling
+**                     TTY if possible -- this should only be done when
+**                     setting up the daemon since otherwise UUCP can
+**                     leave us trying to open a dialin, and we will
+**                     wait for the carrier.
 **
 **     Returns:
 **
 **     Returns:
-**             e.
+**             none
 **
 **     Side Effects:
 **
 **     Side Effects:
-**             none.
+**             Trys to insure that we are immune to vagaries of
+**             the controlling tty.
 */
 
 */
 
-ENVELOPE *
-newenvelope(e)
-       register ENVELOPE *e;
+disconnect(fulldrop)
+       bool fulldrop;
 {
 {
-       bmove((char *) CurEnv, (char *) e, sizeof *e);
-       e->e_header = NULL;
-       e->e_queueup = FALSE;
-       e->e_oldstyle = FALSE;
-       e->e_retreceipt = FALSE;
-       e->e_sendreceipt = FALSE;
-       e->e_origfrom = NULL;
-       e->e_to = NULL;
-       e->e_sendqueue = NULL;
-       e->e_errorqueue = NULL;
-       e->e_parent = CurEnv;
-       e->e_df = NULL;
-
-       return (e);
+       int fd;
+
+#ifdef DEBUG
+       if (tTd(52, 1))
+               printf("disconnect: In %d Out %d\n", fileno(InChannel),
+                                               fileno(OutChannel));
+       if (tTd(52, 5))
+       {
+               printf("don't\n");
+               return;
+       }
+#endif DEBUG
+
+       /* be sure we don't get nasty signals */
+       signal(SIGHUP, SIG_IGN);
+       signal(SIGINT, SIG_IGN);
+       signal(SIGQUIT, SIG_IGN);
+
+       /* we can't communicate with our caller, so.... */
+       HoldErrs = TRUE;
+       ErrorMode = EM_MAIL;
+       Verbose = FALSE;
+
+       /* all input from /dev/null */
+       if (InChannel != stdin)
+       {
+               (void) fclose(InChannel);
+               InChannel = stdin;
+       }
+       (void) freopen("/dev/null", "r", stdin);
+
+       /* output to the transcript */
+       if (OutChannel != stdout)
+       {
+               (void) fclose(OutChannel);
+               OutChannel = stdout;
+       }
+       if (CurEnv->e_xfp == NULL)
+               CurEnv->e_xfp = fopen("/dev/null", "w");
+       (void) fflush(stdout);
+       (void) close(1);
+       (void) close(2);
+       while ((fd = dup(fileno(CurEnv->e_xfp))) < 2 && fd > 0)
+               continue;
+
+#ifdef TIOCNOTTY
+       /* drop our controlling TTY completely if possible */
+       if (fulldrop)
+       {
+               fd = open("/dev/tty", 2);
+               if (fd >= 0)
+               {
+                       (void) ioctl(fd, TIOCNOTTY, 0);
+                       (void) close(fd);
+               }
+       }
+#endif TIOCNOTTY
+
+# ifdef LOG
+       if (LogLevel > 11)
+               syslog(LOG_DEBUG, "in background, pid=%d", getpid());
+# endif LOG
+
+       errno = 0;
 }
 }