home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (c) 1983 Eric P. Allman
- * Copyright (c) 1988 Regents of the University of California.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted provided
- * that: (1) source distributions retain this entire copyright notice and
- * comment, and (2) distributions including binaries display the following
- * acknowledgement: ``This product includes software developed by the
- * University of California, Berkeley and its contributors'' in the
- * documentation or other materials provided with the distribution and in
- * all advertising materials mentioning features or use of this software.
- * Neither the name of the University nor the names of its contributors may
- * be used to endorse or promote products derived from this software without
- * specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- */
-
- #ifndef lint
- # define IN_SCCS_ID
- # define _DEFINE
- char copyright[] =
- "@(#) Copyright (c) 1988 Regents of the University of California.\n\
- All rights reserved.\n";
- static char sccsid[] = "@(#)main.c 5.31 (Berkeley) 7/20/90";
- static char rcsid[] = "@(#)$Id: main.c,v 5.29.0.36 1991/08/06 18:17:12 paul Exp $";
- # ifdef __GNUC__
- static char compiled[] = "@(#)compiled by gcc version "__VERSION__;
- # endif /* __GNUC__ */
- #endif /* not lint */
-
- #ifdef AIX
- # include <sys/utsname.h>
- # undef newstr /* a structure name in sys/utsname.h under RT/AIX */
- #endif /* AIX */
- #include "sendmail.h"
- #if defined(HAS_UNAME) && !defined(AIX)
- # include <sys/utsname.h>
- #endif /* HAS_UNAME && !AIX */
- #include <sgtty.h>
- #ifdef NAMED_BIND
- # include <arpa/nameser.h>
- # include <resolv.h>
- #endif /* NAMED_BIND */
- #ifndef MAXHOSTNAMELEN
- # define MAXHOSTNAMELEN 64
- #endif /* !MAXHOSTNAMELEN */
-
- #ifdef lint
- int edata, end;
- #endif /* lint */
-
- #ifdef __STDC__
- static SIG_TYPE intsig();
- static void initmacros();
- static void freeze(char *);
- static thaw(char *);
- #else /* !__STDC__ */
- static SIG_TYPE intsig();
- static void initmacros();
- static void freeze();
- static thaw();
- #endif /* __STDC__ */
-
- /*
- ** SENDMAIL -- Post mail to a set of destinations.
- **
- ** This is the basic mail router. All user mail programs should
- ** call this routine to actually deliver mail. Sendmail in
- ** turn calls a bunch of mail servers that do the real work of
- ** delivering the mail.
- **
- ** Sendmail is driven by tables read in from /usr/lib/sendmail.cf
- ** (read by readcf.c). Some more static configuration info,
- ** including some code that you may want to tailor for your
- ** installation, is in conf.c. You may also want to touch
- ** daemon.c (if you have some other IPC mechanism), acct.c
- ** (to change your accounting), names.c (to adjust the name
- ** server mechanism).
- **
- ** Usage:
- ** /usr/lib/sendmail [flags] addr ...
- **
- ** See the associated documentation for details.
- **
- ** Author:
- ** 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.
- */
-
-
- int NextMailer; /* "free" index into Mailer struct */
- char *FullName; /* sender's full name */
- char *MatchRecipient;/* for selective queue runs on Recipients */
- char *MatchSender; /* for selective queue runs on Senders */
- char *QueueID; /* run a certain message in the queue */
- ENVELOPE BlankEnvelope; /* a "blank" envelope */
- ENVELOPE MainEnvelope; /* the envelope around the basic letter */
- ADDRESS NullAddress = /* a null address */
- { "", "", NULL, "" };
-
- /*
- ** Pointers for setproctitle.
- ** This allows "ps" listings to give more useful information.
- ** These must be kept out of BSS for frozen configuration files
- ** to work.
- */
-
- #ifdef SETPROCTITLE
- char **Argv = NULL; /* pointer to argument vector */
- char *LastArgv = NULL; /* end of argv */
- #endif /* SETPROCTITLE */
-
- #ifdef DAEMON
- # ifndef SMTP
- ERROR %%%% Cannot have daemon mode without SMTP %%%% ERROR
- # endif /* SMTP */
- #endif /* DAEMON */
-
- void
- main(argc, argv, envp)
- int argc;
- char **argv;
- char **envp;
- {
- register char *p;
- char **av;
- extern char Version[];
- char *from;
- typedef int (*fnptr)();
- STAB *st;
- register int i;
- bool readconfig = TRUE;
- bool queuemode = FALSE; /* process queue requests */
- bool NoName = FALSE;
- bool nothaw;
- static bool reenter = FALSE;
- char jbuf[MAXHOSTNAMELEN+1]; /* holds MyHostName */
- #if defined(SETPROCTITLE) && !defined(SYSV)
- char *UserEnviron[MAXUSERENVIRON+1]; /* saved user environment */
- extern char **environ;
- #endif /* SETPROCTITLE && !SYSV */
-
- /*
- ** 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;
-
- #if defined(notdef) && !defined(SYSV)
- /* Enforce use of local time */
- (void) unsetenv("TZ");
- #endif /* notdef && !SYSV */
-
- /* Make mail act uniformly (resolver recursion disabled elsewhere) */
- (void) unsetenv("HOSTALIASES");
- (void) unsetenv("LOCALDOMAIN");
-
- /*
- ** Be sure we have enough file descriptors.
- ** But also be sure that 0, 1, & 2 are open.
- */
-
- i = open("/dev/null", O_RDWR);
- while (i >= 0 && i < 2)
- i = dup(i);
- #if defined(XPG3)
- for (i = (int) sysconf (_SC_OPEN_MAX); i > 2; --i)
- #else /* !XPG3 */
- for (i = getdtablesize(); i > 2; --i)
- #endif /* XPG3 */
- (void) close(i);
- errno = 0;
-
- #ifdef LOG
- # ifdef LOG_MAIL
- openlog("sendmail", LOG_PID, LOG_MAIL);
- # else /* !LOG_MAIL */
- openlog("sendmail", LOG_PID);
- # endif /* LOG_MAIL */
- #endif /* LOG */
-
- /*
- ** Set default values for variables.
- ** These cannot be in initialized data space.
- */
-
- setdefaults();
-
- /* set up the blank envelope */
- BlankEnvelope.e_puthdr = putheader;
- BlankEnvelope.e_putbody = putbody;
- BlankEnvelope.e_xfp = NULL;
- STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
- CurEnv = &BlankEnvelope;
- STRUCTCOPY(NullAddress, MainEnvelope.e_from);
-
- argv[argc] = NULL;
- av = argv;
- nothaw = FALSE;
-
- /*
- ** 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.
- */
- while ((p = *++av) != NULL)
- {
- if (strncmp(p, "-C", 2) == 0)
- {
- ConfFile = &p[2];
- if (ConfFile[0] == '\0')
- ConfFile = "sendmail.cf";
- (void) setgid(getrgid());
- (void) setuid(getruid());
- nothaw = TRUE;
- }
- else if (strncmp(p, "-bz", 3) == 0)
- nothaw = TRUE;
- else if (strncmp(p, "-bd", 3) == 0 || strncmp(p, "-q", 2) == 0)
- NoName = TRUE;
- else if (strncmp(p, "-Z", 2) == 0)
- {
- #ifdef _PATH_SENDMAILFC
- FreezeFile = &p[2];
- if (FreezeFile[0] == '\0')
- FreezeFile = "sendmail.fc";
- (void) setgid(getrgid());
- (void) setuid(getruid());
- #else /* !_PATH_SENDMAILFC */
- /* Use printf since OutChannel isn't assigned yet */
- printf("Frozen configuration files not available\n");
- #endif /* _PATH_SENDMAILFC */
- }
- else if (strncmp(p, "-d", 2) == 0)
- {
- tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
- tTflag(&p[2]);
- #if defined(XPG3)
- setvbuf(stdout, (char *) NULL, _IOLBF, BUFSIZ);
- #else /* !XPG3 */
- # if defined(SYSV)
- setbuf(stdout, (char *)NULL);
- # else /* !SYSV */
- setlinebuf(stdout);
- # endif /* SYSV */
- #endif /* XPG3 */
- printf("Version %s\n", Version);
- }
- }
-
- InChannel = stdin;
- OutChannel = stdout;
-
- /*
- * Copy the environment only if a successful thaw() is done
- * and/or the environment will be munged later by setproctitle().
- */
- #ifdef _PATH_SENDMAILFC
- if (!nothaw)
- readconfig = !thaw(FreezeFile);
- #endif /* _PATH_SENDMAILFC */
-
- #if defined(SETPROCTITLE) && !defined(SYSV)
- for (i = 0; i < MAXUSERENVIRON && envp[i] != NULL; i++)
- UserEnviron[i] = newstr(envp[i]);
- UserEnviron[i] = NULL;
- environ = UserEnviron;
-
- /*
- ** Save start and extent of argv for setproctitle.
- */
-
- Argv = argv;
- if (i > 0)
- LastArgv = envp[i - 1] + strlen(envp[i - 1]);
- else
- LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
- #endif /* SETPROCTITLE && !SYSV */
-
- #ifdef NAMED_BIND
- /*
- ** Make sure the resolver library is initialized and that enough time
- ** is allowed for non-local servers.
- */
- res_init();
- _res.retrans = 30;
- #endif /* NAMED_BIND */
-
- if (signal(SIGINT, SIG_IGN) != (SIG_TYPE (*)()) SIG_IGN)
- (void) signal(SIGINT, intsig);
- if (signal(SIGHUP, SIG_IGN) != (SIG_TYPE (*)()) SIG_IGN)
- (void) signal(SIGHUP, intsig);
- (void) signal(SIGTERM, intsig);
- (void) signal(SIGPIPE, SIG_IGN);
- OldUmask = umask(0);
- OpMode = MD_DELIVER;
- MotherPid = getpid();
- srand(MotherPid);
- FullName = (NoName) ? NULL : getenv("NAME");
-
- errno = 0;
- from = NULL;
-
- if (readconfig)
- {
- /* initialize some macros, etc. */
- initmacros();
-
- /* hostname */
- av = myhostname(jbuf, sizeof jbuf);
- if (jbuf[0] != '\0')
- {
- #ifdef HAS_UNAME
- struct utsname utsname;
- #endif /* HAS_UNAME */
- if (tTd(0, 4))
- printf("canonical name: %s\n", jbuf);
- p = newstr(jbuf);
- define('w', p, CurEnv);
- setclass('w', p);
- if ((p = index(jbuf, '.')) != NULL)
- *p = '\0';
- makelower(jbuf);
- #ifdef HAS_UNAME
- if ((uname(&utsname) != -1) &&
- strncmp(utsname.nodename, jbuf, 8))
- {
- define('k', newstr(utsname.nodename), CurEnv);
- if (tTd(0, 4))
- printf("UUCP nodename: %s\n",
- utsname.nodename);
- }
- else
- #endif /* HAS_UNAME */
- define('k', newstr(jbuf), CurEnv);
- }
- while (av != NULL && *av != NULL)
- {
- if (tTd(0, 4))
- printf("\ta.k.a.: %s\n", *av);
- setclass('w', *av++);
- }
-
- /* version */
- define('v', Version, CurEnv);
- }
-
- /* current time */
- define('b', arpadate((char *) NULL), CurEnv);
-
- /*
- ** Crack argv.
- */
-
- 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;
- else if (strcmp(p, "smtpd") == 0)
- OpMode = MD_DAEMON;
- else if (strcmp(p, "bsmtp") == 0)
- OpMode = MD_BSMTP;
- while ((p = *++av) != NULL && p[0] == '-')
- {
- switch (p[1])
- {
- case 'b': /* operations mode */
- switch (p[2])
- {
- case MD_DAEMON:
- #ifdef DAEMON
- if (getuid() != 0)
- {
- usrerr("Permission denied");
- exit (EX_USAGE);
- }
- #else /* !DAEMON */
- usrerr("Daemon mode not implemented");
- ExitStat = EX_USAGE;
- break;
- #endif /* DAEMON */
- case MD_SMTP:
- case MD_BSMTP:
- #ifndef SMTP
- usrerr("I don't speak SMTP");
- ExitStat = EX_USAGE;
- 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:
- usrerr("Invalid operation mode %c", p[2]);
- ExitStat = EX_USAGE;
- break;
- }
- break;
-
- case 'C': /* select configuration file (already done) */
- break;
-
- case 'Z': /* select frozen config file (already done) */
- break;
-
- case 'd': /* debugging -- redo in case frozen */
- tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
- tTflag(&p[2]);
- tTflag(&p[2]);
- #if defined(XPG3)
- setvbuf(stdout, (char *) NULL, _IOLBF, BUFSIZ);
- #else /* !XPG3 */
- # if defined(SYSV)
- setbuf(stdout, (char *)NULL);
- # else /* !SYSV */
- setlinebuf(stdout);
- # endif /* SYSV */
- #endif /* XPG3 */
- #ifdef NAMED_BIND
- if (tTd(8, 8))
- _res.options |= RES_DEBUG;
- #endif /* NAMED_BIND */
- break;
-
- case 'f': /* from address */
- case 'r': /* obsolete -f flag */
- p += 2;
- if (*p == '\0' && ((p = *++av) == NULL || *p == '-'))
- {
- p = *++av;
- if (p == NULL || *p == '-')
- {
- usrerr("No \"from\" person");
- ExitStat = EX_USAGE;
- av--;
- break;
- }
- }
- if (from != NULL)
- {
- usrerr("More than one \"from\" person");
- ExitStat = EX_USAGE;
- break;
- }
- from = newstr(p);
- break;
-
- case 'F': /* set full name */
- p += 2;
- if (*p == '\0' && ((p = *++av) == NULL || *p == '-'))
- {
- usrerr("Bad -F flag");
- ExitStat = EX_USAGE;
- av--;
- break;
- }
- FullName = newstr(p);
- break;
-
- case 'h': /* hop count */
- p += 2;
- if (*p == '\0' && ((p = *++av) == NULL || !isdigit(*p)))
- {
- usrerr("Bad hop count (%s)", p);
- ExitStat = EX_USAGE;
- av--;
- break;
- }
- CurEnv->e_hopcount = atoi(p);
- break;
-
- case 'n': /* don't alias */
- NoAlias = TRUE;
- break;
-
- case 'o': /* set option */
- setoption(p[2], &p[3], FALSE, TRUE);
- break;
-
- case 'q': /* run queue files at intervals */
- case 'R': /* run queued messages matching pattern */
- case 'S': /* run queued messages matching pattern */
- case 'M': /* run queue message with this queue ID */
- /* or if 'a' < p[2] <= 'z', set the macro */
- /* this is worse than hideous, blame DECnet */
- if (p[1] == 'M' && p[2] != '\0' && islower(p[2]) &&
- p[2] > 'a')
- {
- setoption(p[1], &p[2], FALSE, TRUE);
- break;
- }
- #ifdef QUEUE
- {
- char c = p[1];
-
- p += 2;
- if (getuid() != 0)
- {
- usrerr("Permission denied");
- exit (EX_USAGE);
- }
- if (queuemode)
- {
- usrerr("Use only one of -q, -R, -S, and -M");
- exit (EX_USAGE);
- }
- if (c != 'q')
- {
- if (*p == '\0' && ((p = *++av) == NULL ||
- *p == '-'))
- {
- usrerr("Bad -%c flag", c);
- ExitStat = EX_USAGE;
- av--;
- break;
- }
- }
- queuemode = TRUE;
- switch (c)
- {
- case 'q':
- QueueIntvl = convtime(p);
- break;
-
- case 'R':
- MatchRecipient = newstr(p);
- break;
-
- case 'S':
- MatchSender = newstr(p);
- break;
-
- case 'M':
- QueueID = newstr(p);
- break;
- }
- }
- #else /* !QUEUE */
- usrerr("I don't know about queues");
- ExitStat = EX_USAGE;
- #endif /* QUEUE */
- break;
-
- case 't': /* read recipients from message */
- GrabTo = TRUE;
- break;
-
- /* 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;
-
- case 's': /* save From lines in headers */
- setoption('f', &p[2], FALSE, TRUE);
- break;
-
- #ifdef DBM
- case 'I': /* initialize alias DBM file */
- OpMode = MD_INITALIAS;
- break;
- #endif /* DBM */
- }
- }
-
- /*
- ** Do basic initialization.
- ** Read system control file.
- ** Extract special fields for local use.
- */
-
- #ifdef BIT8
- {
- char *abuf = newstr("ASCII");
- ascii = getchset(abuf, 29);
- free(abuf);
- }
- #endif /* BIT8 */
-
- if (OpMode == MD_FREEZE || readconfig)
- readcf(ConfFile);
-
- switch (OpMode)
- {
- case MD_FREEZE:
- #ifdef _PATH_SENDMAILFC
- /* this is critical to avoid forgeries of the frozen config */
- (void) setgid(getgid());
- (void) setuid(getuid());
-
- /* freeze the configuration */
- freeze(FreezeFile);
- exit(EX_OK);
- #else /* !_PATH_SENDMAILFC */
- usrerr("Frozen configuration files not available");
- exit(EX_USAGE);
- #endif /* _PATH_SENDMAILFC */
-
- case MD_INITALIAS:
- Verbose = TRUE;
- break;
- }
-
- #ifdef notdef
- /*
- * Print the argument list when debugging. This has to come after the
- * freeze() statement as the first syslog() call sets a state variable
- * that won't get saved or restored.
- */
- {
- char *cmdline = xalloc(MAXLINE);
-
- for (*cmdline = '\0', av = argv; *av; av++)
- {
- (void) strcat(cmdline, *av);
- (void) strcat(cmdline, " ");
- }
- syslog(LOG_DEBUG, "Invoked as: %s", cmdline);
- free(cmdline);
- }
- #endif /* notdef */
-
- /*
- * The call to setdefuser() is moved from readcf.c to here.
- * The result is only used in deliver.c, so delaying it until
- * now should be safe.
- *
- * The function setdefuser() looks up the passwd database. In
- * some systems, notably Suns, this may involve a YP lookup.
- * Unfortunately YP lookups leave behind some state information
- * which seems to be incompatible with the freeze/thaw operations.
- *
- * Placing the call here delays it till after the freeze/thaw
- * is complete.
- */
- setdefuser();
-
- /* 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 */
- expand("\001j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv);
- MyHostName = jbuf;
-
- /* the indices of local and program mailers */
- st = stab("local", ST_MAILER, ST_FIND);
- if (st == NULL)
- syserr("No local mailer defined");
- else
- LocalMailer = st->s_mailer;
- st = stab("prog", ST_MAILER, ST_FIND);
- if (st == NULL)
- syserr("No prog mailer defined");
- else
- ProgMailer = st->s_mailer;
-
- /* operate in queue directory */
- if (chdir(QueueDir) < 0)
- {
- syserr("cannot chdir(%s)", QueueDir);
- exit(EX_SOFTWARE);
- }
-
- /*
- ** Do operation-mode-dependent initialization.
- */
-
- switch (OpMode)
- {
- case MD_PRINT:
- /* print the queue */
- #ifdef QUEUE
- dropenvelope(CurEnv);
- printqueue();
- exit(EX_OK);
- #else /* !QUEUE */
- usrerr("No queue to print");
- finis();
- #endif /* QUEUE */
-
- case MD_INITALIAS:
- /* initialize alias database */
- initaliases(TRUE);
- exit(EX_OK);
-
- case MD_DAEMON:
- /* don't open alias database -- done in srvrsmtp */
- break;
-
- default:
- #if !defined(DBM_AUTOBUILD) || ( !defined(NDBM) && !defined(OTHERDBM) )
- /* open the alias database */
- initaliases(FALSE);
- #endif /* !DBM_AUTOBUILD || (!NDBM && !OTHERDBM) */
- break;
- }
-
- if (tTd(0, 15))
- {
- /* print configuration table (or at least part of it) */
- printrules();
- for (i = 0; i < MAXMAILERS; i++)
- {
- register struct mailer *m = Mailer[i];
- int j;
-
- if (m == NULL)
- continue;
- printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld F=",
- i, m->m_name, m->m_mailer,
- m->m_se_rwset, m->m_sh_rwset,
- m->m_re_rwset, m->m_rh_rwset, m->m_maxsize);
- for (j = '\0'; j <= '\177'; j++)
- if (bitnset(j, m->m_flags))
- (void) putchar(j);
- printf(" E=");
- xputs(m->m_eol);
- printf("\n");
- }
- }
-
- /*
- ** Switch to the main envelope.
- */
-
- CurEnv = newenvelope(&MainEnvelope);
- MainEnvelope.e_flags = BlankEnvelope.e_flags;
-
- if (tTd(3, 1))
- (void) getla(); /* prints load average in getla() */
-
- /*
- ** If test mode, read addresses from stdin and process.
- */
-
- if (OpMode == MD_TEST)
- {
- char buf[MAXLINE];
-
- printf("ADDRESS TEST MODE\nEnter <ruleset> <address>\n");
- printf("[Note: No initial ruleset 3 call]\n");
- for (;;)
- {
- register char **pvp;
- char *q;
- extern char *DelimChar;
-
- printf("> ");
- (void) 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';
- if (invalidaddr(p+1))
- continue;
- do
- {
- char pvpbuf[PSBUFSIZE];
-
- pvp = prescan(++p, '\0', pvpbuf);
- 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 we should also be processing the queue, start
- ** doing it in background.
- ** We check for any errors that might have happened
- ** during startup.
- */
-
- if (OpMode == MD_DAEMON || QueueIntvl != 0)
- {
- if (!tTd(0, 1))
- {
- /* put us in background */
- i = fork();
- if (i < 0)
- syserr("daemon: cannot fork");
- if (i != 0)
- exit(0);
-
- /* get our pid right */
- MotherPid = getpid();
-
- /* disconnect from our controlling tty */
- disconnect(TRUE);
- }
-
- #ifdef QUEUE
- if (queuemode)
- {
- runqueue(TRUE);
- if (OpMode != MD_DAEMON)
- for (;;)
- pause();
- }
- #endif /* QUEUE */
- dropenvelope(CurEnv);
-
- #ifdef DAEMON
- getrequests();
-
- /* at this point we are in a child: reset state */
- OpMode = MD_SMTP;
- (void) newenvelope(CurEnv);
- openxscript(CurEnv);
- #endif /* DAEMON */
- }
-
- #ifdef SMTP
- /*
- ** If running SMTP protocol, start collecting and executing
- ** commands. This will never return.
- */
-
- if (OpMode == MD_SMTP || OpMode == MD_BSMTP)
- {
- bool batched = (OpMode == MD_BSMTP);
- OpMode = MD_SMTP;
-
- /* have to run unbuffered or else will lose synchronization */
- if (batched)
- setbuf(InChannel, (char *) NULL);
- smtp(batched);
- }
- #endif /* SMTP */
-
- /*
- ** Do basic system initialization and set the sender
- */
-
- initsys();
- setsender(from);
-
- if (OpMode != MD_ARPAFTP && *av == NULL && !GrabTo)
- {
- usrerr("Recipient names must be specified");
-
- /* collect body for UUCP return */
- if (OpMode != MD_VERIFY)
- collect(FALSE);
- finis();
- }
- if (OpMode == MD_VERIFY)
- SendMode = SM_VERIFY;
-
- /*
- ** Scan argv and deliver the message to everyone.
- */
-
- sendtoargv(av);
-
- /* if we have had errors sofar, arrange a meaningful exit stat */
- if (Errors > 0 && ExitStat == EX_OK)
- ExitStat = EX_USAGE;
-
- /*
- ** Read the input mail.
- */
-
- CurEnv->e_to = NULL;
- if (OpMode != MD_VERIFY || GrabTo)
- collect(FALSE);
- errno = 0;
-
- if (tTd(1, 1))
- printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
-
- /*
- ** Actually send everything.
- ** If verifying, just ack.
- */
-
- CurEnv->e_from.q_flags |= QDONTSEND;
- CurEnv->e_to = NULL;
- sendall(CurEnv, SM_DEFAULT);
-
- /* collect statistics (done after sendall, since sendall may fork) */
- if (OpMode != MD_VERIFY)
- markstats(CurEnv, (ADDRESS *) NULL);
-
- /*
- ** All done.
- */
-
- finis();
- }
- /*
- ** FINIS -- Clean up and exit.
- **
- ** Parameters:
- ** none
- **
- ** Returns:
- ** never
- **
- ** Side Effects:
- ** exits sendmail
- */
-
- void
- finis()
- {
- if (tTd(2, 1))
- printf("\n====finis: stat %d e_flags %o\n", ExitStat, CurEnv->e_flags);
-
- /* clean up temp files */
- CurEnv->e_to = NULL;
- dropenvelope(CurEnv);
-
- /* post statistics */
- poststats(StatFile);
-
- /* 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);
- }
- /*
- ** INTSIG -- clean up on interrupt
- **
- ** This just arranges to exit. It pessimises in that it
- ** may resend a message.
- **
- ** Parameters:
- ** none.
- **
- ** Returns:
- ** none.
- **
- ** Side Effects:
- ** Unlocks the current job.
- */
-
- static SIG_TYPE
- intsig()
- {
- FileName = NULL;
- unlockqueue(CurEnv);
- exit(EX_OK);
- }
- /*
- ** INITMACROS -- initialize the macro system
- **
- ** This just involves defining some macros that are actually
- ** used internally as metasymbols to be themselves.
- **
- ** Parameters:
- ** none.
- **
- ** Returns:
- ** none.
- **
- ** Side Effects:
- ** initializes several macros to be themselves.
- */
-
- struct metamac MetaMacros[] =
- {
- /* LHS pattern matching characters */
- '*', MATCHZANY, '+', MATCHANY, '-', MATCHONE, '=', MATCHCLASS,
- '~', MATCHNCLASS, '%', MATCHMAP, '^', MATCHNMAP,
-
- /* these are RHS metasymbols */
- '#', CANONNET, '@', CANONHOST, ':', CANONUSER, '>', CALLSUBR,
-
- /* the conditional operations */
- '?', CONDIF, '|', CONDELSE, '.', CONDFI,
-
- /* and finally the hostname and database lookup characters */
- '[', HOSTBEGIN, ']', HOSTEND,
- '(', KEYBEGIN, ')', KEYEND,
-
- #ifdef MACVALUE
- /* run-time macro expansion, not at freeze time */
- '&', MACVALUE,
- #endif /* MACVALUE */
- #ifdef QUOTE822
- '!', QUOTE822, /* quote next macro if RFC822 requires it */
- #endif /* QUOTE822 */
- '\0'
- };
-
- static void
- initmacros()
- {
- register struct metamac *m;
- char buf[5];
- register int c;
-
- for (m = MetaMacros; m->metaname != '\0'; m++)
- {
- 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);
- }
- }
-
- #ifdef _PATH_SENDMAILFC
- /*
- ** FREEZE -- freeze BSS & allocated memory
- **
- ** This will be used to efficiently load the configuration file.
- **
- ** Parameters:
- ** freezefile -- the name of the file to freeze to.
- **
- ** Returns:
- ** none.
- **
- ** Side Effects:
- ** Writes BSS and malloc'ed memory to freezefile
- */
-
- union frz
- {
- char frzpad[BUFSIZ]; /* insure we are on a BUFSIZ boundary */
- struct
- {
- TIME_TYPE frzstamp; /* timestamp on this freeze */
- char *frzbrk; /* the current break */
- char *frzedata; /* address of edata */
- char *frzend; /* address of end */
- char frzver[252]; /* sendmail version */
- char frzdatecompiled[64]; /* sendmail compilation date */
- } frzinfo;
- };
-
- static void
- freeze(freezefile)
- char *freezefile;
- {
- int f;
- union frz fhdr;
- extern int edata, end;
- #ifdef __STDC__
- extern void *sbrk(int);
- #else /* !__STDC__ */
- extern char *sbrk();
- #endif /* __STDC__ */
- extern char Version[];
- extern char datecompiled[];
-
- if (freezefile == NULL)
- return;
- # ifdef notdef
- {
- /*
- * NIS (nee YP) state is saved in the freeze, the readback
- * of the freeze file destroys similar information. This
- * causes strange results later due to the unexpected closing
- * of a file descriptor using a stale copy.
- */
-
- char *domain;
- if (yp_get_default_domain(&domain) == 0)
- yp_unbind(domain);
- }
- # endif /* YP */
-
- /* try to open the freeze file */
- f = creat(freezefile, FileMode);
- if (f < 0)
- {
- syserr("Cannot freeze %s", freezefile);
- errno = 0;
- return;
- }
-
- /* build the freeze header */
- fhdr.frzinfo.frzstamp = curtime();
- fhdr.frzinfo.frzbrk = sbrk(0);
- fhdr.frzinfo.frzedata = (char *) &edata;
- fhdr.frzinfo.frzend = (char *) &end;
- (void) strcpy(fhdr.frzinfo.frzver, Version);
- (void) strcpy(fhdr.frzinfo.frzdatecompiled, datecompiled);
-
- /* write out the freeze header */
- if (write(f, (char *) &fhdr, sizeof fhdr) != sizeof fhdr ||
- write(f, (char *) &edata, (int) (fhdr.frzinfo.frzbrk - (char *) &edata)) !=
- (int) (fhdr.frzinfo.frzbrk - (char *) &edata))
- {
- syserr("Cannot freeze %s", freezefile);
- }
-
- /* fine, clean up */
- (void) close(f);
- }
- /*
- ** THAW -- read in the frozen configuration file.
- **
- ** Parameters:
- ** freezefile -- the name of the file to thaw from.
- **
- ** Returns:
- ** TRUE if it successfully read the freeze file.
- ** FALSE otherwise.
- **
- ** Side Effects:
- ** reads freezefile in to BSS area.
- */
-
- static int
- thaw(freezefile)
- char *freezefile;
- {
- int f;
- union frz fhdr;
- extern int edata, end;
- extern char Version[];
- extern char datecompiled[];
- #ifdef __STDC__
- extern int brk(void *);
- #else /* !__STDC__ */
- extern int brk();
- #endif /* __STDC__ */
-
- if (freezefile == NULL)
- return (FALSE);
-
- /* open the freeze file */
- f = open(freezefile, 0);
- if (f < 0)
- {
- # ifdef LOG
- syslog(LOG_NOTICE, "Cannot open frozen config file %s: %m",
- freezefile);
- # endif /* LOG */
- errno = 0;
- return (FALSE);
- }
- # ifdef notdef
- {
- /*
- * NIS (nee YP) state is saved in the freeze, the readback
- * of the freeze file destroys similar information. This
- * causes strange results later due to the unexpected closing
- * of a file descriptor using a stale copy.
- */
-
- char *domain;
- if (yp_get_default_domain(&domain) == 0)
- yp_unbind(domain);
- }
- # endif /* YP */
-
- /* read in the header */
- if (read(f, (char *) &fhdr, sizeof fhdr) < sizeof fhdr)
- {
- syserr("Cannot read frozen config file");
- (void) close(f);
- return (FALSE);
- }
- {
- char why[40];
-
- *why = '\0';
- if (fhdr.frzinfo.frzedata != (char *) &edata)
- (void) strcat(why, "Edata");
- if (fhdr.frzinfo.frzend != (char *) &end)
- (void) strcat(why, "End");
- if (strcmp(fhdr.frzinfo.frzver, Version))
- (void) strcat(why, "Version");
- if (strcmp(fhdr.frzinfo.frzdatecompiled, datecompiled))
- (void) strcat(why, "Datecompiled");
- if (*why)
- {
- if (tTd(0, 1))
- printf("Wrong version of frozen config file (%s mismatch)\n", why);
- # ifdef LOG
- syslog(LOG_WARNING, "Wrong version of frozen config file (%s mismatch)", why);
- # endif /* LOG */
- (void) close(f);
- return (FALSE);
- }
- }
-
- /* arrange to have enough space */
- if (brk(fhdr.frzinfo.frzbrk) == -1)
- {
- syserr("Cannot break to %x", fhdr.frzinfo.frzbrk);
- (void) close(f);
- return (FALSE);
- }
-
- /* now read in the freeze file */
- if (read(f, (char *) &edata, (int) (fhdr.frzinfo.frzbrk - (char *) &edata)) !=
- (int) (fhdr.frzinfo.frzbrk - (char *) &edata))
- {
- syserr("Cannot read frozen config file");
- /* oops! we have trashed memory..... */
- (void) write(2, "Cannot read freeze file\n", 24);
- _exit(EX_SOFTWARE);
- }
-
- (void) close(f);
- return (TRUE);
- }
- #endif /* _PATH_SENDMAILFC */
- /*
- ** DISCONNECT -- remove our connection with any foreground process
- **
- ** Parameters:
- ** 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:
- ** none
- **
- ** Side Effects:
- ** Trys to insure that we are immune to vagaries of
- ** the controlling tty.
- */
-
- void
- disconnect(fulldrop)
- bool fulldrop;
- {
- int fd;
-
- if (tTd(52, 1))
- printf("disconnect: In %d Out %d\n", fileno(InChannel),
- fileno(OutChannel));
- if (tTd(52, 5))
- {
- printf("don't\n");
- return;
- }
-
- /* be sure we don't get nasty signals */
- (void) signal(SIGHUP, SIG_IGN);
- (void) signal(SIGINT, SIG_IGN);
- (void) signal(SIGQUIT, SIG_IGN);
-
- /* we can't communicate with our caller, so.... */
- HoldErrs = TRUE;
- setoption('e', "m", TRUE, TRUE);
- 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;
-
- /* drop our controlling TTY completely if possible */
- if (fulldrop)
- {
- #if defined(XPG3)
- /*
- * setsid will provide a new session w/o any tty associated at all.
- * STANDARDS CONFORMANCE
- * setpgrp: SVID2, XPG2
- * setsid: XPG3, POSIX.1, FIPS 151-1
- */
- (void) setsid();
- #else /* !XPG3 */
- # if BSD > 43
- daemon(1, 1);
- # else /* BSD <= 43 */
- # ifdef TIOCNOTTY
- fd = open("/dev/tty", 2);
- if (fd >= 0)
- {
- (void) ioctl(fd, (int) TIOCNOTTY, (char *) 0);
- (void) close(fd);
- }
- (void) setpgrp(0, 0);
- # endif /* TIOCNOTTY */
- # endif /* BSD */
- # if !defined(BSD) && !defined(TIOCNOTTY) && defined(SYSV)
- setpgrp();
- if (fork() != 0)
- exit(0);
- # endif /* !BSD && !TIOCNOTTY && SYSV */
- #endif /* XPG3 */
- errno = 0;
- }
-
- #ifdef LOG
- if (LogLevel > 11)
- syslog(LOG_DEBUG, "in background, pid=%d", getpid());
- #endif /* LOG */
-
- errno = 0;
- }
-